Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[discussion] Static Perl compilation without PAR + make optional / drop more dependencies #338

Closed
vadimkantorov opened this issue Oct 5, 2020 · 67 comments

Comments

@vadimkantorov
Copy link

vadimkantorov commented Oct 5, 2020

Hi! I'm building yet-another-attempt-of-latex-in-the-browser: https://vadimkantorov.github.io/busytext/busytex.html. So far I managed to build xetex+bibtex8.

I was thinking to somehow try to "compile" biber into WebAssembly. I was considering two options:

  1. perlcc to generate C code
  2. WebPerl to replace PAR packing

What would be your thoughts on feasibility of these two variants? Have you tried using perlcc on biber? Are there any "nasty" required dependencies?

Does biber require popen / signals / web requests for normal functioning? If not, it probably can be made working in WASM.

@plk
Copy link
Owner

plk commented Oct 6, 2020

I would be interested in helping to look into this. perlcc could work as it's an established system. For WebPerl, biber does use http requests to fetch external data sources and this might be an issue I suppose. Another potential issue is the btparse library that is used by Text::BibTeX as this is very old and I don't know how this would play with modern frameworks like this. I do some minor signal messing about in biber to contain some segfaults from libbtparse for example.

@vadimkantorov
Copy link
Author

Cool :) I'll post here my building attempts

@vadimkantorov
Copy link
Author

vadimkantorov commented Oct 12, 2020

As part of prep work, I'm working on compiling cperl (a small Perl compiler that supports perlcc in a more stable way) statically with all modules in order to run tlmgr from C code (a smaller Perl codebase than biber). If that works, that could be an alternative way to WebPerl (WebPerl does same for regular Perl), and it would also run natively.

If you're interested to also work on this, feel free to slide into perl11/cperl#423 (comment)

@vadimkantorov
Copy link
Author

An update:

I wanted to see if one can get a fully static build (Emscripten has a dynamic linking support, but it's new; and a static build is valuable on its own for embedding in other programs).

  1. I first checked if cperl + perlcc are able to produce a fully static build. At the moment they cannot, the advancement is blocked: If ExifTool is used, binary compiled using perlcc depends on Config.so and cwd.so, even if --static/--staticxs is used. perl11/cperl#423
  2. I failed to install RPerl that might also work - CPAN installs was taking too much time and producing too many errors on my system
  3. https://metacpan.org/pod/distribution/App-Staticperl/staticperl.pod may work, needs to be checked (it configures the main perl distro for a static build)
  4. WebPerl approach of getting a static build - probably similar to staticperl, may work outside of WebAssembly context as well

@vadimkantorov
Copy link
Author

vadimkantorov commented Oct 17, 2020

@plk What is the minimum set of biber's dependencies for a minimial local-only working setup? (without downloading stuff from internet, without running external programs) Full list from Build.pl: autovivification Class::Accessor Data::Dump Data::Compare Data::Uniqid DateTime::Format::Builder DateTime::Calendar::Julian File::Slurper IPC::Cmd IPC::Run3 List::AllUtils List::MoreUtils List::MoreUtils::XS Mozilla::CA Regexp::Common Log::Log4perl Unicode::Collate Unicode::Normalize Unicode::LineBreak Unicode::GCString Encode::Locale Encode::EUCJPASCII Encode::JIS2K Encode::HanExtra Parse::RecDescent PerlIO::utf8_strict XML::LibXML XML::LibXML::Simple XML::LibXSLT XML::Writer Sort::Key Storable Text::CSV Text::CSV_XS Text::Roman IO::String URI Text::BibTeX LWP::UserAgent LWP::Protocol::https Business::ISBN Business::ISSN Business::ISMN Lingua::Translit

@plk
Copy link
Owner

plk commented Oct 17, 2020

That is the minimum dependencies you have listed there ...

@vadimkantorov
Copy link
Author

E.g. Mozilla::CA or LWP should not be needed in theory if consulting internet is disabled, right?

Same for IPC. Does biber require running external programs for minimal functioning?

@plk
Copy link
Owner

plk commented Oct 17, 2020

Well, I'd have to see whether things would work without them as their integration isn't really that modular as it was never designed to be used without them.

@vadimkantorov
Copy link
Author

vadimkantorov commented Oct 18, 2020

Okay! In the meanwhile I'll try to build it with all dependencies. but for sure it'd be more robust for these cross-compilation scenarios (and have smaller binary size!) if there is a mode "no-calling-external-programs + no-downloading-from-internet" that enables running even if LWP/IPC are not installed/present

@vadimkantorov
Copy link
Author

vadimkantorov commented Nov 28, 2020

Do you know how to skip tests during ./Build installdeps? Doing CPAN_OPTS=-T ./Build installdeps does not help. Otherwise installing all dependencies is very slow.

It may be that CPAN_OPTS handling is broken in cpan. So a switch in ./Build or ./Build.pl to enable cpan -T instead of cpan would be nice.

@vadimkantorov
Copy link
Author

That is the minimum dependencies you have listed there ...

Why does the https://github.com/plk/biber/blob/dev/dist/linux_x86_64/build.sh PAR run command contain much fewer dependencies? namely, only

  --module=Pod::Simple::TranscodeSmart \
  --module=Pod::Simple::TranscodeDumb \
  --module=List::MoreUtils::XS \
  --module=List::SomeUtils::XS \
  --module=List::MoreUtils::PP \
  --module=HTTP::Status \
  --module=HTTP::Date \
  --module=Encode:: \
  --module=File::Find::Rule \
  --module=IO::Socket::SSL \
  --module=IO::String \
  --module=PerlIO::utf8_strict \
  --module=Text::CSV_XS \
  --module=DateTime \

@vadimkantorov
Copy link
Author

Also, I'm trying to set up a Github Actions build script in https://github.com/vadimkantorov/buildbiber/blob/master/.github/workflows/build.yml

Maybe it would be good for biber to have it for some testing as well in the main repo

@plk
Copy link
Owner

plk commented Nov 28, 2020

Do you know how to skip tests during ./Build installdeps? Doing CPAN_OPTS=-T ./Build installdeps does not help. Otherwise installing all dependencies is very slow.

It may be that CPAN_OPTS handling is broken in cpan. So a switch in ./Build or ./Build.pl to enable cpan -T instead of cpan would be nice.

Did you try ./Build installdeps --cpan_client 'cpan -T'

@vadimkantorov
Copy link
Author

Did you try ./Build installdeps --cpan_client 'cpan -T'

Not yet. Will try!

@plk
Copy link
Owner

plk commented Nov 28, 2020

That is the minimum dependencies you have listed there ...

Why does the https://github.com/plk/biber/blob/dev/dist/linux_x86_64/build.sh PAR run command contain much fewer dependencies? namely, only

  --module=Pod::Simple::TranscodeSmart \
  --module=Pod::Simple::TranscodeDumb \
  --module=List::MoreUtils::XS \
  --module=List::SomeUtils::XS \
  --module=List::MoreUtils::PP \
  --module=HTTP::Status \
  --module=HTTP::Date \
  --module=Encode:: \
  --module=File::Find::Rule \
  --module=IO::Socket::SSL \
  --module=IO::String \
  --module=PerlIO::utf8_strict \
  --module=Text::CSV_XS \
  --module=DateTime \

It does depend on the platform - it's somewhat empirical, you have to tweak the build script until the executable works in terms of module includes. This is because the dependency checking modules are quite sensitive to platforms and the idiosyncracies of particular perl builds. If in doubt, use a module line to explicitly import - it can't hurt. I usually start with a basic set from a similar OS and then add as needed which I test the binary and see that it didn't auto-detect pack a module that's needed.

@plk
Copy link
Owner

plk commented Nov 28, 2020

Regarding testing, I haven't looked this in github as it required setting up a perl environment etc. and that's probably not trivial ...

@vadimkantorov
Copy link
Author

They have an Ubuntu installation, but without a perl distribution pre-installed.

I opted for compiling one from scratch (since going forward I'd like to replace it by a custom statically-built WebAssembly one):

name: build

on: workflow_dispatch

env:
  URL: https://www.cpan.org/src/5.0/perl-5.32.0.tar.gz
  MAKEFLAGS: -j2

jobs:
  build:
    runs-on: ubuntu-20.04
    steps:
       - name: Install Perl
         run: |
           echo Downloading and compiling in [$PWD] from [$URL]

           mkdir perl
           wget -nc $URL
           tar -xf $(basename $URL) --strip-components=1 --directory=perl

           pushd perl
           bash +x ./Configure -sde -Dprefix="$PWD/../prefix"
           test -f Makefile
           make
           make install
           popd

       - name: Install Biber Dependencies Without Test
         run: |
           BEFORE=$(find ./prefix | wc -l)
           ./prefix/bin/cpan -T Module::Build Config::AutoConf ExtUtils::LibBuilder    autovivification Class::Accessor Data::Dump Data::Compare Data::Uniqid DateTime::Format::Builder DateTime::Calendar::Julian File::Slurper IPC::Cmd IPC::Run3 List::AllUtils List::MoreUtils List::MoreUtils::XS Mozilla::CA Regexp::Common Log::Log4perl Unicode::Collate Unicode::Normalize Unicode::LineBreak Unicode::GCString Encode::Locale Encode::EUCJPASCII Encode::JIS2K Encode::HanExtra Parse::RecDescent PerlIO::utf8_strict XML::LibXML XML::LibXML::Simple XML::LibXSLT XML::Writer Sort::Key Storable Text::CSV Text::CSV_XS Text::Roman IO::String URI Text::BibTeX LWP::UserAgent LWP::Protocol::https Business::ISBN Business::ISSN Business::ISMN Lingua::Translit
           AFTER=$(find ./prefix | wc -l)
           echo files before: $BEFORE after: $AFTER

@vadimkantorov
Copy link
Author

Running https://github.com/vadimkantorov/buildbiber/blob/master/.github/workflows/build.yml, essentially:

# perl and cpan are built from sources

cpan -T Test::More Test::Differences File::Which    Module::Build    Config::AutoConf ExtUtils::LibBuilder    autovivification Class::Accessor Data::Dump Data::Compare Data::Uniqid DateTime::Format::Builder DateTime::Calendar::Julian File::Slurper IPC::Cmd IPC::Run3 List::AllUtils List::MoreUtils List::MoreUtils::XS Mozilla::CA Regexp::Common Log::Log4perl Unicode::Collate Unicode::Normalize Unicode::LineBreak Unicode::GCString Encode::Locale Encode::EUCJPASCII Encode::JIS2K Encode::HanExtra Parse::RecDescent PerlIO::utf8_strict XML::LibXML XML::LibXML::Simple XML::LibXSLT XML::Writer Sort::Key Storable Text::CSV Text::CSV_XS Text::Roman IO::String URI Text::BibTeX LWP::UserAgent LWP::Protocol::https Business::ISBN Business::ISSN Business::ISMN Lingua::Translit

URLBIBER=https://github.com/plk/biber/archive/v2.15.tar.gz
URLTESTFILES=https://master.dl.sourceforge.net/project/biblatex-biber/biblatex-biber/testfiles
BIBERTESTFILES="test.bib test.bcf test-dev.bcf unifont.ttf"

mkdir biber
wget $URLBIBER
tar -xf $(basename $URLBIBER) --strip-components=1 --directory biber

pushd biber
perl ./Build.PL
perl ./Build install

wget $(printf "$URLTESTFILES/%s " $BIBERTESTFILES)
perl ./Build test
perl ./bin/biber --validate-control --convert-control test

produced a lot of sortinithash mismatches. Is there a way to install the specific version of Unicode::Collate to get rid of them?

I attach the full test log log.txt

@plk
Copy link
Owner

plk commented Nov 28, 2020

What version of perl and U::C is on the test box?

@vadimkantorov
Copy link
Author

I install there perl 5.32.0 from sources in https://github.com/Perl/perl5/tree/v5.32.0

I could easily upgrade the perl version there if needed by just specifying a different version

@vadimkantorov
Copy link
Author

I don't know what U::C version it installs from CPAN. If a particular U::C version is required, I can check if possible to install a specific version from CPAN

@plk
Copy link
Owner

plk commented Nov 28, 2020

Do you know what version of Unicode::Collate is there?

@plk
Copy link
Owner

plk commented Nov 28, 2020

I see - I think that my install is not using the latest U::C - let me update and update the test results and then let's see. It would be best to currently use perl 5.30 for the tests until I migrate all the builds/tests to 5.32. Ah, I see the sortinithash issues with U::C 1.29 will fix in the tests in DEV branch and update here.

@vadimkantorov
Copy link
Author

In all likelihood it's:

./prefix/bin/cpan -D Unicode::Collate
Reading '/home/vadimkantorov/.cpan/Metadata'
  Database was generated on Sat, 28 Nov 2020 14:17:03 GMT
Unicode::Collate
-------------------------------------------------------------------------
        (no description)
        S/SA/SADAHIRO/Unicode-Collate-1.29.tar.gz
        /home/vadimkantorov/perl/../prefix/lib/5.32.0/x86_64-linux/Unicode/Collate.pm
        Installed: 1.27
        CPAN:      1.29  Not up to date
        SADAHIRO Tomoyuki (SADAHIRO)
        [email protected]

@vadimkantorov
Copy link
Author

If it matters, the latest release at https://github.com/Perl/perl5/releases is 5.33.4

@plk
Copy link
Owner

plk commented Nov 28, 2020

I never use odd numbers as they tend to be considered experimental. 5.32 would be the latest stable version.

@vadimkantorov
Copy link
Author

My github workflow file doesn't do PAR packing, since I didn't need it, but if it's useful for a single beginner/simple/free_CI build script, please feel free to build upon it

@plk
Copy link
Owner

plk commented Nov 28, 2020

DEV branch is updated with requirement for U::C 1.29 and all tests should have the correct sortinithashes for this now.

@vadimkantorov
Copy link
Author

Can PAR output the final list of resolved module dependencies? I'm advancing with compiling WebPerl, so a full list of modules to try bundling with Perl at build time would be very useful.

@plk
Copy link
Owner

plk commented Nov 29, 2020

Have a look at the docs for Module::ScanDeps - that's what PAR uses to resolve dependencies. The -module lines in the par script are simply there to catch things that this module can't detect (usually due to runtime includes etc.)

@vadimkantorov
Copy link
Author

I've retried again. Here is the list of PAR-discovered dependencies:

./shlib/x86_64-linux/libcrypto.so.3
./shlib/x86_64-linux/libexslt.so.0
./shlib/x86_64-linux/libbtparse.so
./shlib/x86_64-linux/libssl.so.3
./shlib/x86_64-linux/libxslt.so.1
./shlib/x86_64-linux/libz.so.1
./shlib/x86_64-linux/libxml2.so.2
+ ./lib/auto/I18N/Langinfo/Langinfo.so
+ ./lib/auto/Digest/MD5/MD5.so
./lib/auto/Sort/Key/Key.so
./lib/auto/Compress/Raw/Zlib/Zlib.so
./lib/auto/Compress/Raw/Bzip2/Bzip2.so
+ ./lib/auto/Encode/Encode.so
./lib/auto/Encode/Unicode/Unicode.so
./lib/auto/Encode/CN/CN.so
./lib/auto/Encode/KR/KR.so
./lib/auto/Encode/EUCJPASCII/EUCJPASCII.so
./lib/auto/Encode/Symbol/Symbol.so
./lib/auto/Encode/EBCDIC/EBCDIC.so
./lib/auto/Encode/Byte/Byte.so
./lib/auto/Encode/TW/TW.so
./lib/auto/Encode/HanExtra/HanExtra.so
./lib/auto/Encode/JP/JP.so
./lib/auto/Encode/JIS2K/JIS2K.so
./lib/auto/autovivification/autovivification.so
./lib/auto/Devel/Caller/Caller.so
./lib/auto/Devel/LexAlias/LexAlias.so
./lib/auto/XML/LibXSLT/LibXSLT.so
./lib/auto/XML/Parser/Expat/Expat.so
./lib/auto/XML/LibXML/LibXML.so
./lib/auto/Unicode/Normalize/Normalize.so
./lib/auto/Unicode/LineBreak/LineBreak.so
./lib/auto/Unicode/Collate/Collate.so
./lib/auto/IPC/SysV/SysV.so
./lib/auto/Clone/Clone.so
+ ./lib/auto/B/B.so
./lib/auto/Text/CSV_XS/CSV_XS.so
./lib/auto/Text/BibTeX/BibTeX.so
./lib/auto/PadWalker/PadWalker.so
+ ./lib/auto/PerlIO/encoding/encoding.so
+ ./lib/auto/PerlIO/scalar/scalar.so
+ ./lib/auto/PerlIO/via/via.so
./lib/auto/PerlIO/utf8_strict/utf8_strict.so
./lib/auto/PerlIO/mmap/mmap.so
./lib/auto/MIME/Base64/Base64.so
./lib/auto/HTML/Parser/Parser.so
./lib/auto/File/DosGlob/DosGlob.so
+ ./lib/auto/File/Glob/Glob.so
./lib/auto/threads/shared/shared.so
./lib/auto/threads/threads.so
./lib/auto/Time/HiRes/HiRes.so
./lib/auto/Sys/Syslog/Syslog.so
./lib/auto/Sys/Hostname/Hostname.so
+ ./lib/auto/Opcode/Opcode.so
+ ./lib/auto/Data/Dumper/Dumper.so
+ ./lib/auto/attributes/attributes.so
./lib/auto/IO/Compress/Brotli/Brotli.so
+ ./lib/auto/IO/IO.so
./lib/auto/List/MoreUtils/XS/XS.so
./lib/auto/List/SomeUtils/XS/XS.so
+ ./lib/auto/List/Util/Util.so
./lib/auto/Filter/Util/Call/Call.so
./lib/auto/DBI/DBI.so
./lib/auto/Net/SSLeay/SSLeay.so
./lib/auto/re/re.so
+ ./lib/auto/Fcntl/Fcntl.so
./lib/auto/Socket/Socket.so
./lib/auto/Sub/Identify/Identify.so
./lib/auto/DateTime/DateTime.so
+ ./lib/auto/mro/mro.so
./lib/auto/Storable/Storable.so
./lib/auto/Variable/Magic/Magic.so
./lib/auto/Math/BigInt/FastCalc/FastCalc.so
./lib/auto/Class/XSAccessor/XSAccessor.so
./lib/auto/Hash/Util/FieldHash/FieldHash.so
./lib/auto/DBD/SQLite/SQLite.so
./lib/auto/Package/Stash/XS/XS.so
+ ./lib/auto/Cwd/Cwd.so
./lib/auto/Params/Validate/XS/XS.so
./lib/auto/Params/Util/Util.so
+ ./lib/auto/POSIX/POSIX.so

@vadimkantorov
Copy link
Author

vadimkantorov commented Mar 11, 2024

@plk Btw I managed to build statically all these *.so package dependencies with a simple enough script

So there might be path towards building statically all of biber with musl - without any packer utils.

Maybe a remaining question for more portability is: does biber use backticks/system shell calls? If so, for portability it would be better to replace them with native Perl function calls or at least localize all such system shell calls in one perl source file to ease inspection and replacing them with more portable Perl code in the future.

This can also open path to providing biber as a library (if needed)

@plk
Copy link
Owner

plk commented Mar 11, 2024

No, no backticks. Any such thing is done with OS neutral perl modules by design.

@vadimkantorov
Copy link
Author

This is great news, as I found that tlmgr.pl / install-tl.pl are using backticks all over the place...

@plk
Copy link
Owner

plk commented Mar 11, 2024

SInce biber has to run on WIndows, Mac and Linix, a lot of effort was put in to make sure it played nicely with all of them in a non hacky way ....

@vadimkantorov
Copy link
Author

vadimkantorov commented Sep 6, 2024

@plk I succeded in compiling a fully static variant of Perl (without any .so) - https://github.com/vadimkantorov/perlpack

I'll at some point try to use it instead of PAR to compile/pack a biber - without any temporary file extraction and so on, everything is embedded in a read-only, virtual FS (including *.pm files and any other files needed)

@vadimkantorov
Copy link
Author

@plk
Copy link
Owner

plk commented Oct 28, 2024

I only use the AMBS/Text-BibTeX package which includes a modified libbtparse which I have added to over the years.

@vadimkantorov
Copy link
Author

https://github.com/ambs/Text-BibTeX right? or some other place from CTAN/CPAN?

@plk
Copy link
Owner

plk commented Oct 28, 2024

Yes, that's it. That's just the dev site for the package than ends up in CPAN which is where I pull it from for the build.

@vadimkantorov
Copy link
Author

@plk
Copy link
Owner

plk commented Oct 28, 2024

Exactly, that's libbtparse with additions to support biber (some bug fixes and basic UTF8 support). It's a very old library but we managed to modify it to pass through UTF8 so that we can handle it all in Perl.

@vadimkantorov
Copy link
Author

@plk Where testfiles are stored now? https://sourceforge.net/projects/biblatex-biber/files/biblatex-biber/testfiles/ now has only unifont.ttf?

I used to do

wget https://master.dl.sourceforge.net/project/biblatex-biber/biblatex-biber/testfiles/test.bib https://master.dl.sourceforge.net/project/biblatex-biber/biblatex-biber/testfiles/test.bcf https://master.dl.sourceforge.net/project/biblatex-biber/biblatex-biber/testfiles/test-dev.bcf https://master.dl.sourceforge.net/project/biblatex-biber/biblatex-biber/testfiles/unifont.ttf
./biber-linux_x86_64 --validate-control --convert-control test

but how to run tests now?

@plk
Copy link
Owner

plk commented Oct 29, 2024

Ah, sorry, they were moved into the git repo so that they are more up to date. For the DEV branch:

https://github.com/plk/biber/tree/dev/testfiles

@vadimkantorov
Copy link
Author

vadimkantorov commented Oct 29, 2024

this is nice! curious, why unifont.ttf is remaining on sourceforge? could also be moved to github repo or github releases? (arbitrary files can be served off a github release) - and thus deprecating the old sourceforge page

@plk
Copy link
Owner

plk commented Oct 30, 2024

I forgot to delete it ...

@vadimkantorov
Copy link
Author

vadimkantorov commented Nov 22, 2024

I somehow made it work with a fully static Perl build (instead of using PAR): https://github.com/busytex/busybiber/blob/main/.github/workflows/busybiber.yml <- in this file you can see how many dependencies currently biber has... It would be nice to be able to know which ones are actually not needed and remove them.

I'm embedding all Perl and data modules inside the executable. And somehow getting an error Cannot find XML::LibXSLT stylesheet. Skipping conversion Which file is it referring to?

How can I check that biber can discover all the bundled data files / stylesheets? Is there some command checking for this?

@plk
Copy link
Owner

plk commented Nov 22, 2024

Well, that error will tell you if you can find it or not. Sometimes you have to explicitly package files in the pp call to make sure they are in the package.

@vadimkantorov
Copy link
Author

I made some fixes. Is this log looking correct now?

INFO - This is Biber 2.20
INFO - Logfile is 'test.blg'
INFO - Converted BibLaTeX control file 'test.bcf' to 'test.bcf.html'
INFO - Reading 'test.bcf'
INFO - Using all citekeys in bib section 0
INFO - Processing section 0
INFO - Looking for bibtex file 'test.bib' for section 0
INFO - LaTeX decoding ...
INFO - Found BibTeX data source 'test.bib'
INFO - Datamodel validation starting
INFO - Datamodel validation complete
INFO - Overriding locale 'en-US' defaults 'variable = shifted' with 'variable = non-ignorable'
INFO - Overriding locale 'en-US' defaults 'normalization = NFD' with 'normalization = prenormalized'
INFO - Sorting list 'nty/global//global/global/global' of type 'entry' with template 'nty' and locale 'en-US'
INFO - No sort tailoring available for locale 'en-US'
INFO - Writing 'test.bbl' with encoding 'UTF-8'
INFO - Output to test.bbl

@plk
Copy link
Owner

plk commented Nov 24, 2024

Looks good as long as there is something in the .html.

@vadimkantorov
Copy link
Author

Yeah, test.bcf.html is in there

@vadimkantorov
Copy link
Author

vadimkantorov commented Nov 24, 2024

So essentially what I completed successfully is:

  • on Alpine (which ships with musl libc and plenty of static library packages)
  • I compiled a static perl single-file binary, containining all the required module files (and overriding a few of I/O functions to let these be read by Perl when it requests them) without using PAR, instead with direct compiling of a Perl runner with static perl modules and libperl
  • the binary is fully static (no dynamic libraries are depended on, including libc) and sort of portable, as I'm linking with musl libc and all other libraries statically

If we can reduce the number of library dependencies and Perl dependencies, this might be a PAR-less, simpler way of building. At least for some setups, this might be interesting.

@vadimkantorov
Copy link
Author

vadimkantorov commented Nov 26, 2024

To be precise, in attempt to match all the .so files included in biber, I'm currently linking a lot of stuff:

$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Class/XSAccessor/XSAccessor.a

$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Params/Util/Util.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Params/Validate/XS/XS.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/XML/LibXML/LibXML.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/XML/Parser/Expat/Expat.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/XML/LibXSLT/LibXSLT.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/DateTime/DateTime.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Text/CSV_XS/CSV_XS.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Text/BibTeX/BibTeX.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Variable/Magic/Magic.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/IO/Compress/Brotli/Brotli.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/IO/Compress/Brotli/libbrotlidec.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/IO/Compress/Brotli/libbrotlienc.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/IO/Compress/Brotli/libbrotlicommon.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/HTML/Parser/Parser.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Package/Stash/XS/XS.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/PadWalker/PadWalker.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Net/SSLeay/SSLeay.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/List/SomeUtils/XS/XS.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/List/MoreUtils/XS/XS.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Unicode/LineBreak/LineBreak.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Devel/LexAlias/LexAlias.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Devel/Caller/Caller.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Storable/Storable.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/autovivification/autovivification.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/DBD/SQLite/SQLite.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/DBI/DBI.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Sub/Identify/Identify.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Sort/Key/Key.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/PerlIO/utf8_strict/utf8_strict.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Clone/Clone.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Encode/JIS2K/JIS2K.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Encode/HanExtra/HanExtra.a 
$PERLPREFIX/lib/perl5/site_perl/$PERLVER/x86_64-linux/auto/Encode/EUCJPASCII/EUCJPASCII.a

$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/mro/mro.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/File/Glob/Glob.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/File/DosGlob/DosGlob.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/I18N/Langinfo/Langinfo.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/SDBM_File/SDBM_File.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Compress/Raw/Zlib/Zlib.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Compress/Raw/Bzip2/Bzip2.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/POSIX/POSIX.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Fcntl/Fcntl.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Data/Dumper/Dumper.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Math/BigInt/FastCalc/FastCalc.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/IO/IO.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Opcode/Opcode.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Socket/Socket.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/threads/shared/shared.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/threads/threads.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/MIME/Base64/Base64.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Time/HiRes/HiRes.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Time/Piece/Piece.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Filter/Util/Call/Call.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/List/Util/Util.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Unicode/Collate/Collate.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Unicode/Normalize/Normalize.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/B/B.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Devel/Peek/Peek.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Storable/Storable.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/attributes/attributes.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/re/re.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Hash/Util/Util.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Hash/Util/FieldHash/FieldHash.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/IPC/SysV/SysV.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/PerlIO/encoding/encoding.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/PerlIO/mmap/mmap.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/PerlIO/via/via.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Sys/Hostname/Hostname.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Sys/Syslog/Syslog.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Encode/KR/KR.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Encode/JP/JP.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Encode/Byte/Byte.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Encode/CN/CN.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Encode/Encode.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Encode/Unicode/Unicode.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Encode/Symbol/Symbol.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Encode/EBCDIC/EBCDIC.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Encode/TW/TW.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Cwd/Cwd.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Digest/MD5/MD5.a 
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/auto/Digest/SHA/SHA.a

$PERLPREFIX/lib/libbtparse.a
$PERLPREFIX/lib/perl5/$PERLVER/x86_64-linux/CORE/libperl.a
/usr/lib/libxslt.a
/usr/lib/libexslt.a
/usr/lib/libexpat.a
/usr/lib/libcrypto.a /usr/lib/libgcrypt.a /usr/lib/libgpg-error.a
/usr/lib/libxml2.a
/lib/libz.a
/usr/lib/liblzma.a
/usr/lib/libssl.a

It would be good to know which ones are actually not needed - then it would allow to reduce a lot the disk footprint of the final binary... E.g. not very clear to me why libcrypto/libssl are needed for functioning of biber

Here is the original file list I got from the biber:
unzip.txt

@vadimkantorov
Copy link
Author

vadimkantorov commented Jan 17, 2025

Just for reference, here is my complete github workflow for building Biber in standard PAR-way and with a static Perl build into a single-file binary embedding all data files inside the binary.

Some future work directions:

  • reducing the number of actually needed Perl modules (especially those with *.a C extensions)
  • reducing the number of xml lib dependencies or other dependencies if they can be done without
  • maybe upstreaming some complete GitHub workflows like these into the main repo
  • file size of static Perl build matches PAR, even a bit smaller
  • for reference, here are the libraries corresponding to the embedded .sos in the PAR version (and it's quite a lot of dependencies, any reduction would be helpful!):
    • libxslt.a
    • libexslt.a
    • libexpat.a
    • libcrypto.a
    • libgcrypt.a
    • libgpg-error.a
    • libxml2.a
    • libz.a
    • liblzma.a
    • libssl.a
name: build-biber

on: workflow_dispatch
# https://github.com/plk/biber/blob/dev/dist/linux_x86_64/build.sh
env:
  BIBER_DEV_TESTS: 0
  PERLVER: "5.40.0"
  PERLPLATFORM: "x86_64-linux"
  URLPERL: https://www.cpan.org/src/5.0/perl-5.40.0.tar.gz
  URL_Biber: https://github.com/plk/biber/archive/v2.20.tar.gz
  URL_Params_Validate_XS : https://cpan.metacpan.org/authors/id/D/DR/DROLSKY/Params-Validate-1.31.tar.gz
  URL_Text_BibTeX        : https://cpan.metacpan.org/authors/id/A/AM/AMBS/Text-BibTeX-0.89.tar.gz
  URL_IO_Compress_Brotli : https://cpan.metacpan.org/authors/id/T/TI/TIMLEGGE/IO-Compress-Brotli-0.017.tar.gz
  
jobs:
  dynamicbiber:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      
      - name: Install Prerequisites
        run: sudo apt-get install libxml2-dev libz-dev libxslt-dev
      
      - name: Install Perl and build, pack Biber
        #shell: bash
        # https://github.com/plk/biber/blob/dev/dist/linux_x86_64/build.sh
        run: |
          mkdir perlsourcedynamic && curl -L $URLPERL | tar -xzf - --strip-components=1 --directory=perlsourcedynamic
          cd perlsourcedynamic && sh +x ./Configure -sde -Dprefix="$PWD/../perlprefixdynamic" && make && make install && cd ..
          export PATH="$PWD/perlprefixdynamic/bin:$PATH"
          export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$PWD/perlprefixdynamic/lib"
          
          mkdir biberpp && curl -L $URL_Biber | tar -xzf - --strip-components=1 --directory=biberpp && cd biberpp
          ../perlprefixdynamic/bin/cpan -T Module::Build PAR::Packer
          ../perlprefixdynamic/bin/perl   Build.PL
          ../perlprefixdynamic/bin/perl ./Build installdeps --cpan_client "$PWD/../perlprefixdynamic/bin/cpan -T"
          ../perlprefixdynamic/bin/perl ./Build test
          ../perlprefixdynamic/bin/perl ./Build install
          # bash ./dist/linux_x86_64/build.sh
          ucpath="$PWD/../perlprefixdynamic/lib/$PERLVER/Unicode/Collate"
          echo "USING Unicode::Collate at: ${ucpath}"
          PAR_VERBATIM=1 $PWD/../perlprefixdynamic/bin/pp \
            --module=deprecate \
            --module=Biber::Input::file::bibtex \
            --module=Biber::Input::file::biblatexml \
            --module=Biber::Output::dot \
            --module=Biber::Output::bbl \
            --module=Biber::Output::bblxml \
            --module=Biber::Output::bibtex \
            --module=Biber::Output::biblatexml \
            --module=Pod::Simple::TranscodeSmart \
            --module=Pod::Simple::TranscodeDumb \
            --module=List::MoreUtils::XS \
            --module=List::SomeUtils::XS \
            --module=List::MoreUtils::PP \
            --module=HTTP::Status \
            --module=HTTP::Date \
            --module=Encode:: \
            --module=File::Find::Rule \
            --module=IO::Socket::SSL \
            --module=IO::String \
            --module=PerlIO::utf8_strict \
            --module=Text::CSV_XS \
            --module=DateTime \
            --link=$PWD/../perlprefixdynamic/lib/libbtparse.so \
            --link=/usr/lib/$PERLPLATFORM-gnu/libxml2.so \
            --link=/usr/lib/$PERLPLATFORM-gnu/libz.so \
            --link=/usr/lib/$PERLPLATFORM-gnu/libxslt.so \
            --link=/usr/lib/$PERLPLATFORM-gnu/libexslt.so \
            --link=/usr/lib/$PERLPLATFORM-gnu/libssl.so \
            --link=/usr/lib/$PERLPLATFORM-gnu/libcrypto.so \
            --addfile="data/biber-tool.conf;lib/Biber/biber-tool.conf" \
            --addfile="data/schemata/config.rnc;lib/Biber/config.rnc" \
            --addfile="data/schemata/config.rng;lib/Biber/config.rng"\
            --addfile="data/schemata/bcf.rnc;lib/Biber/bcf.rnc" \
            --addfile="data/schemata/bcf.rng;lib/Biber/bcf.rng" \
            --addfile="lib/Biber/LaTeX/recode_data.xml;lib/Biber/LaTeX/recode_data.xml" \
            --addfile="data/bcf.xsl;lib/Biber/bcf.xsl" \
            --addfile="${ucpath}/Locale;lib/Unicode/Collate/Locale" \
            --addfile="${ucpath}/CJK;lib/Unicode/Collate/CJK" \
            --addfile="${ucpath}/allkeys.txt;lib/Unicode/Collate/allkeys.txt" \
            --addfile="${ucpath}/keys.txt;lib/Unicode/Collate/keys.txt" \
            --addfile="$PWD/../perlprefixdynamic/lib/site_perl/$PERLVER/Mozilla/CA/cacert.pem;lib/Mozilla/CA/cacert.pem" \
            --addfile="$PWD/../perlprefixdynamic/lib/$PERLVER/$PERLPLATFORM-thread-multi/PerlIO;lib/PerlIO" \
            --addfile="$PWD/../perlprefixdynamic/lib/$PERLVER/$PERLPLATFORM-thread-multi/auto/PerlIO;lib/auto/PerlIO" \
            --addfile="$PWD/../perlprefixdynamic/lib/site_perl/$PERLVER/Business/ISBN/RangeMessage.xml;lib/Business/ISBN/RangeMessage.xml" \
            --cachedeps=scancache \
            --output=biber \
            $PWD/../perlprefixdynamic/bin/biber
          ./biber --help
          unzip -l ./biber
          cd testfiles && ../biber --validate-datamodel --convert-control test && test -f test.bcf.html

      - name: Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: dynamicbiber
          path: biberpp/biber

  staticbiber:
    runs-on: ubuntu-24.04
    container: alpine:3.14
    steps:
      - name: Install Prerequisites
        run:  |
          apk add --update --no-cache libnsl libnsl-dev build-base coreutils gdb cmake git xz curl gperf p7zip zip autoconf automake libtool pkgconfig gnupg libxml2-dev libxslt-dev expat-dev openssl-dev openssl  zlib-static expat-static wget
          apk add --update --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/edge/main libxml2-static libxslt-static openssl-libs-static xz-static libgcrypt-static libgpg-error-static

      - uses: actions/checkout@v4
      
      - name: perlpack.pl
        run: |
          cat <<'EOF' > perlpack.pl
          use strict;
          use warnings;
          use Getopt::Long;
          use File::Path;
          use File::Find;
          use File::Spec;
          use Cwd;
          
          my $input_path = '';
          my $output_path = '';
          my $prefix = '';
          my $ld = 'ld';
          my $include = '';
          my $exclude = '';
          Getopt::Long::GetOptions(
              'input-path|i=s'      => \$input_path,
              'output-path|o=s'     => \$output_path,
              'prefix=s'            => \$prefix,
              'ld=s'                => \$ld,
              'include=s'           => \$include,
              'exclude=s'           => \$exclude
          );
          
          die "Input path does not exist or is not a directory" unless -e $input_path && -d $input_path ;
          die "Output path not specified" if $output_path eq '';
          
          $output_path = Cwd::abs_path($output_path);
          my $output_path_o = $output_path . '.o';
          File::Path::make_path($output_path_o);
          my (@objects, @relpaths_dirs, @safepaths, @relpaths);
              
          # problem: can produce the same symbol name because of this mapping, ld maps only to _, so may need to rename the file before invoking ld
          my %translate = ('.' => '_', '-' => '__', '_' => '_', '/' => '_');
          my $translate_keys = join("", keys %translate);
          
          my $oldcwd = Cwd::getcwd();
          File::Find::find(sub {
              my $newcwd = Cwd::getcwd(); chdir($oldcwd); 
              my $p = $File::Find::name;
              
              my $relpath = $p;
              if (index($relpath, $input_path) == 0) { $relpath = substr($relpath, length($input_path)); }
              if (index($relpath, '/') == 0) { $relpath = substr($relpath, 1); }
              my $safepath = ''; 
              for my $char (split //, $relpath) { $safepath .= exists $translate{$char} ? $translate{$char} : $char; }
          
              my $include_file = 1;
              if (-d $p) {
                  push @relpaths_dirs, $p;
                  $include_file = 0;
              } elsif ($include ne '' and $p =~ /$include/) {
                  $include_file = 1;
              } elsif ($exclude ne '' and $p =~ /$exclude/) {
                  $include_file = 0;
              } elsif (substr($relpath, -2) eq '.o') {
                  $include_file = 0;
              }
              
              if ($include_file) {
                  push @safepaths, $safepath;
                  push @relpaths, $relpath;
                  push @objects, File::Spec->catfile($output_path_o, $safepath . '.o');
                  
                  chdir($output_path_o);
                  symlink(File::Spec->catfile($oldcwd, $p), $safepath);
                  system($ld, '-r', '-b', 'binary', '-o', $objects[-1], $safepath) == 0 or die "ld command failed: $?";
                  unlink($safepath);
              }
              chdir($newcwd);
          }, $input_path);
          
          open my $g, '>', $output_path . '.txt' or die;
          print $g join("\n", @objects);
          open my $f, '>', $output_path or die;
          print $f "size_t packfs_builtin_files_num = ", scalar(@relpaths), ", packfs_builtin_dirs_num = ", scalar(@relpaths_dirs), ";\n\n";
          print $f "const char* packfs_builtin_abspaths[] = {\n\"" , join("\",\n\"", map { File::Spec->catfile($prefix, $_) } @relpaths), "\"\n};\n\n";
          print $f "const char* packfs_builtin_abspaths_dirs[] = {\n\"" , join("\",\n\"", map { File::Spec->catfile($prefix, $_) } @relpaths_dirs) , "\"\n};\n\n";
          print $f join("\n", map { "extern char _binary_${_}_start[], _binary_${_}_end[];" } @safepaths), "\n\n";
          print $f "const char* packfs_builtin_starts[] = {\n", join("\n", map { "_binary_${_}_start," } @safepaths), "\n};\n\n";
          print $f "const char* packfs_builtin_ends[] = {\n", join("\n", map { "_binary_${_}_end," } @safepaths), "\n};\n\n";
          EOF

      - name: busybiber.c
        run: |
          cat <<'EOF' > busybiber.c
          #define _GNU_SOURCE
          #include <string.h>
          #include <stdio.h>
          #include <unistd.h>
          #include <errno.h>
          #include <stdarg.h>
          #include <fcntl.h>
          #include <stdlib.h>
          
          #include <sys/stat.h>
          #include <sys/mman.h>
          #include <sys/types.h>
          
          #include "perlpack.h"
          //size_t packfs_builtin_files_num, packfs_builtin_dirs_num; const char** packfs_builtin_starts; const char** packfs_builtin_ends; const char** packfs_builtin_abspaths; const char** packfs_builtin_abspaths_dirs;
          
          extern int      __real_open(const char *path, int flags);                               
          extern int      __real_close(int fd);                                                   
          extern ssize_t  __real_read(int fd, void* buf, size_t count);                           
          extern int      __real_access(const char *path, int flags);                             
          extern off_t    __real_lseek(int fd, off_t offset, int whence);                         
          extern int      __real_stat(const char *restrict path, struct stat *restrict statbuf);  
          extern int      __real_fstat(int fd, struct stat * statbuf);                            
          extern FILE*    __real_fopen(const char *path, const char *mode);                       
          extern int      __real_fileno(FILE* stream);                                            
          
          enum {
              packfs_filefd_min = 1000000000, 
              packfs_filefd_max = 1000001000, 
              packfs_filepath_max_len = 256, 
          };
          
          int packfs_enabled = 1;
          int packfs_filefd[packfs_filefd_max - packfs_filefd_min];
          FILE* packfs_fileptr[packfs_filefd_max - packfs_filefd_min];
          size_t packfs_filesize[packfs_filefd_max - packfs_filefd_min];
          
          #define PACKFS_STRING_VALUE_(x) #x
          #define PACKFS_STRING_VALUE(x) PACKFS_STRING_VALUE_(x)
          // TODO: append / if missing
          char packfs_builtin_prefix[] = PACKFS_STRING_VALUE(PACKFS_BUILTIN_PREFIX);
          #undef PACKFS_STRING_VALUE
          #undef PACKFS_STRING_VALUE_
          
          void packfs_sanitize_path(char* path_sanitized, const char* path)
          {
              size_t len = path != NULL ? strlen(path) : 0;
              if(len == 0)
                  path_sanitized[0] = '\0';
          
              for(int i = (path != NULL && len > 2 && path[0] == '.' && path[1] == '/') ? 2 : 0, k = 0; len > 0 && i < len; i++)
              {
                  if(!(i > 1 && path[i] == '/' && path[i - 1] == '/'))
                  {
                      path_sanitized[k++] = path[i];
                      path_sanitized[k] = '\0';
                  }
              }
          }
          
          int packfs_strncmp(const char* prefix, const char* path, size_t count)
          {
              return (prefix != NULL && prefix[0] != '\0' && path != NULL && path[0] != '\0') ? strncmp(prefix, path, count) : 1;
          }
          
          int packfs_open(const char* path, FILE** out)
          {
              char path_sanitized[packfs_filepath_max_len]; packfs_sanitize_path(path_sanitized, path);
          
              FILE* fileptr = NULL;
              size_t filesize = 0;
              
              if(packfs_builtin_files_num > 0 && 0 == packfs_strncmp(packfs_builtin_prefix, path_sanitized, strlen(packfs_builtin_prefix)))
              {
                  for(size_t i = 0; i < packfs_builtin_files_num; i++)
                  {
                      if(0 == strcmp(path_sanitized, packfs_builtin_abspaths[i]))
                      {
                          filesize = (size_t)(packfs_builtin_ends[i] - packfs_builtin_starts[i]);
                          fileptr = fmemopen((void*)packfs_builtin_starts[i], filesize, "r");
                          break;
                      }
                  }
              }
          
              if(out != NULL)
                  *out = fileptr;
          
              for(size_t k = 0; fileptr != NULL && k < packfs_filefd_max - packfs_filefd_min; k++)
              {
                  if(packfs_filefd[k] == 0)
                  {
                      packfs_filefd[k] = packfs_filefd_min + k;
                      packfs_fileptr[k] = fileptr;
                      packfs_filesize[k] = filesize;
                      return packfs_filefd[k];
                  }
              }
          
              return -1;
          }
          
          int packfs_close(int fd)
          {
              if(fd < packfs_filefd_min || fd >= packfs_filefd_max)
                  return -2;
          
              for(size_t k = 0; k < packfs_filefd_max - packfs_filefd_min; k++)
              {
                  if(packfs_filefd[k] == fd)
                  {
                      packfs_filefd[k] = 0;
                      packfs_filesize[k] = 0;
                      int res = fclose(packfs_fileptr[k]);
                      packfs_fileptr[k] = NULL;
                      return res;
                  }
              }
              return -2;
          }
          
          void* packfs_find(int fd, FILE* ptr)
          {
              if(ptr != NULL)
              {
                  for(size_t k = 0; k < packfs_filefd_max - packfs_filefd_min; k++)
                  {
                      if(packfs_fileptr[k] == ptr)
                          return &packfs_filefd[k];
                  }
                  return NULL;
              }
              else
              {
                  if(fd < packfs_filefd_min || fd >= packfs_filefd_max)
                      return NULL;
                  
                  for(size_t k = 0; k < packfs_filefd_max - packfs_filefd_min; k++)
                  {
                      if(packfs_filefd[k] == fd)
                          return packfs_fileptr[k];
                  }
              }
              return NULL;
          }
          
          ssize_t packfs_read(int fd, void* buf, size_t count)
          {
              FILE* ptr = packfs_find(fd, NULL);
              if(!ptr)
                  return -1;
              return (ssize_t)fread(buf, 1, count, ptr);
          }
          
          int packfs_seek(int fd, long offset, int whence)
          {
              FILE* ptr = packfs_find(fd, NULL);
              if(!ptr)
                  return -1;
              return fseek(ptr, offset, whence);
          }
          
          int packfs_access(const char* path)
          {
              char path_sanitized[packfs_filepath_max_len]; packfs_sanitize_path(path_sanitized, path);
          
              if(0 == packfs_strncmp(packfs_builtin_prefix, path_sanitized, strlen(packfs_builtin_prefix)))
              {
                  for(size_t i = 0; i < packfs_builtin_files_num; i++)
                  {
                      if(0 == strcmp(path_sanitized, packfs_builtin_abspaths[i]))
                          return 0;
                  }
                  return -1;
              }
              
              return -2;
          }
          
          int packfs_stat(const char* path, int fd, struct stat *restrict statbuf)
          {
              char path_sanitized[packfs_filepath_max_len]; packfs_sanitize_path(path_sanitized, path);
              
              if(0 == packfs_strncmp(packfs_builtin_prefix, path_sanitized, strlen(packfs_builtin_prefix)))
              {
                  for(size_t i = 0; i < packfs_builtin_files_num; i++)
                  {
                      if(0 == strcmp(path_sanitized, packfs_builtin_abspaths[i]))
                      {
                          *statbuf = (struct stat){0};
                          statbuf->st_size = (off_t)(packfs_builtin_ends[i] - packfs_builtin_starts[i]);
                          statbuf->st_mode = S_IFREG;
                          return 0;
                      }
                  }
                  for(size_t i = 0; i < packfs_builtin_dirs_num; i++)
                  {
                      if(0 == strcmp(path_sanitized, packfs_builtin_abspaths_dirs[i]))
                      {
                          *statbuf = (struct stat){0};
                          statbuf->st_size = 0;
                          statbuf->st_mode = S_IFDIR;
                          return 0;
                      }
                  }
                  return -1;
              }
              
              if(fd >= 0 && packfs_filefd_min <= fd && fd < packfs_filefd_max)
              {
                  for(size_t k = 0; k < packfs_filefd_max - packfs_filefd_min; k++)
                  {
                      if(packfs_filefd[k] == fd)
                      {
                          *statbuf = (struct stat){0};
                          statbuf->st_size = packfs_filesize[k];
                          statbuf->st_mode = S_IFREG;
                          return 0;
                      }
                  }
                  return -1;
              }
          
              return -2;
          }
          
          ///////////
          
          FILE* __wrap_fopen(const char *path, const char *mode)
          {
              if(packfs_enabled)
              {
                  FILE* res = NULL;
                  if(packfs_open(path, &res) >= 0)
                  {
                      return res;
                  }
              }
          
              FILE* res = __real_fopen(path, mode);
              return res;
          }
          
          int __wrap_fileno(FILE *stream)
          {
              int res = __real_fileno(stream);
              
              if(packfs_enabled && res < 0)
              {        
                  int* ptr = packfs_find(-1, stream);
                  res = ptr == NULL ? -1 : (*ptr);
              }
              
              return res;
          }
          
          int __wrap_open(const char *path, int flags, ...)
          {
              if(packfs_enabled)
              {
                  int res = packfs_open(path, NULL);
                  if(res >= 0)
                  { 
                      return res;
                  }
              }
              
              int res = __real_open(path, flags);
              return res;
          }
          
          int __wrap_close(int fd)
          {
              if(packfs_enabled)
              {
                  int res = packfs_close(fd);
                  if(res >= -1)
                  {
                      return res;
                  }
              }
              
              int res = __real_close(fd);
              return res;
          }
          
          
          ssize_t __wrap_read(int fd, void* buf, size_t count)
          {
              if(packfs_enabled)
              {
                  ssize_t res = packfs_read(fd, buf, count);
                  if(res >= 0)
                  {
                      return res;
                  }
              }
          
              ssize_t res = __real_read(fd, buf, count);
              return res;
          }
          
          off_t __wrap_lseek(int fd, off_t offset, int whence)
          {
              if(packfs_enabled)
              {
                  int res = packfs_seek(fd, (long)offset, whence);
                  if(res >= 0)
                  {
                      return res;
                  }
              }
          
              off_t res = __real_lseek(fd, offset, whence);
              return res;
          }
          
          
          int __wrap_access(const char *path, int flags) 
          {
              if(packfs_enabled)
              {
                  int res = packfs_access(path);
                  if(res >= -1)
                      return res;
              }
              
              int res = __real_access(path, flags); 
              return res;
          }
          
          int __wrap_stat(const char *restrict path, struct stat *restrict statbuf)
          {
              if(packfs_enabled)
              {
                  int res = packfs_stat(path, -1, statbuf);
                  if(res >= -1)
                  {
                      return res;
                  }
              }
          
              int res = __real_stat(path, statbuf);
              return res;
          }
          
          int __wrap_fstat(int fd, struct stat * statbuf)
          {
              if(packfs_enabled)
              {
                  int res = packfs_stat(NULL, fd, statbuf);
                  if(res >= -1)
                  {
                      return res;
                  }
              }
              
              int res = __real_fstat(fd, statbuf);
              return res;
          }
          
          #include <EXTERN.h>
          #include <perl.h>
          #include <XSUB.h>
          
          // #include <xsinit.c>
          void xs_init(pTHX) //EXTERN_C 
          {
              static const char file[] = __FILE__;
              dXSUB_SYS;
              PERL_UNUSED_CONTEXT;
              
              extern void boot_DynaLoader(pTHX_ CV* cv); newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
             
              extern void boot_mro(pTHX_ CV* cv); newXS("mro::bootstrap", boot_mro, file);
              extern void boot_Devel__Peek(pTHX_ CV* cv); newXS("Devel::Peek", boot_Devel__Peek, file);
              extern void boot_File__DosGlob(pTHX_ CV* cv); newXS("File::DosGlob::bootstrap", boot_File__DosGlob, file);
              extern void boot_File__Glob(pTHX_ CV* cv); newXS("File::Glob::bootstrap", boot_File__Glob, file);
              extern void boot_Sys__Syslog(pTHX_ CV* cv); newXS("Sys::Syslog::bootstrap", boot_Sys__Syslog, file);
              extern void boot_Sys__Hostname(pTHX_ CV* cv); newXS("Sys::Hostname::bootstrap", boot_Sys__Hostname, file);
              extern void boot_PerlIO__via(pTHX_ CV* cv); newXS("PerlIO::via::bootstrap", boot_PerlIO__via, file);
              extern void boot_PerlIO__mmap(pTHX_ CV* cv); newXS("PerlIO::mmap::bootstrap", boot_PerlIO__mmap, file);
              extern void boot_PerlIO__encoding(pTHX_ CV* cv); newXS("PerlIO::encoding::bootstrap", boot_PerlIO__encoding, file);
              //extern void boot_PerlIO__scalar(pTHX_ CV* cv); newXS("PerlIO::scalar::bootstrap", boot_PerlIO__scalar, file);
              //extern void boot_PerlIO__utf8_strict(pTHX_ CV* cv); newXS("PerlIO::utf8_strict::bootstrap", boot_PerlIO__utf8_strict, file);
              extern void boot_B(pTHX_ CV* cv); newXS("B::bootstrap", boot_B, file);
              extern void boot_attributes(pTHX_ CV* cv); newXS("attributes::bootstrap", boot_attributes, file);
              extern void boot_Unicode__Normalize(pTHX_ CV* cv); newXS("Unicode::Normalize::bootstrap", boot_Unicode__Normalize, file);
              extern void boot_Unicode__Collate(pTHX_ CV* cv); newXS("Unicode::Collate::bootstrap", boot_Unicode__Collate, file);
              extern void boot_Unicode__LineBreak(pTHX_ CV* cv); newXS("Unicode::LineBreak::bootstrap", boot_Unicode__LineBreak, file);
              extern void boot_threads(pTHX_ CV* cv); newXS("threads::bootstrap", boot_threads, file);
              extern void boot_threads__shared(pTHX_ CV* cv); newXS("threads::shared::bootstrap", boot_threads__shared, file);
              extern void boot_IPC__SysV(pTHX_ CV* cv); newXS("IPC::SysV::bootstrap", boot_IPC__SysV, file);
              extern void boot_re(pTHX_ CV* cv); newXS("re::bootstrap", boot_re, file);
              extern void boot_Digest__MD5(pTHX_ CV* cv); newXS("Digest::MD5::bootstrap", boot_Digest__MD5, file);
              extern void boot_Digest__SHA(pTHX_ CV* cv); newXS("Digest::SHA::bootstrap", boot_Digest__SHA, file);
              extern void boot_SDBM_File(pTHX_ CV* cv); newXS("SDBM_File::bootstrap", boot_SDBM_File, file);
              extern void boot_Math__BigInt__FastCalc(pTHX_ CV* cv); newXS("Math::BigInt::FastCalc::bootstrap", boot_Math__BigInt__FastCalc, file);
              extern void boot_Data__Dumper(pTHX_ CV* cv); newXS("Data::Dumper::bootstrap", boot_Data__Dumper, file);
              extern void boot_I18N__Langinfo(pTHX_ CV* cv); newXS("I18N::Langinfo::bootstrap", boot_I18N__Langinfo, file);
              extern void boot_Time__HiRes(pTHX_ CV* cv); newXS("Time::HiRes::bootstrap", boot_Time__HiRes, file);
              extern void boot_Time__Piece(pTHX_ CV* cv); newXS("Time::Piece::bootstrap", boot_Time__Piece, file);
              extern void boot_IO(pTHX_ CV* cv); newXS("IO::bootstrap", boot_IO, file);
              extern void boot_Socket(pTHX_ CV* cv); newXS("Socket::bootstrap", boot_Socket, file);
              extern void boot_Hash__Util__FieldHash(pTHX_ CV* cv); newXS("Hash::Util::FieldHash::bootstrap", boot_Hash__Util__FieldHash, file); 
              extern void boot_Hash__Util(pTHX_ CV* cv); newXS("Hash::Util::bootstrap", boot_Hash__Util, file);
              extern void boot_Filter__Util__Call(pTHX_ CV* cv); newXS("Filter::Util::Call::bootstrap", boot_Filter__Util__Call, file);
              extern void boot_POSIX(pTHX_ CV* cv); newXS("POSIX::bootstrap", boot_POSIX, file);
              extern void boot_Encode__Unicode(pTHX_ CV* cv); newXS("Encode::Unicode::bootstrap", boot_Encode__Unicode, file);
              extern void boot_Encode(pTHX_ CV* cv); newXS("Encode::bootstrap", boot_Encode, file);
              extern void boot_Encode__JP(pTHX_ CV* cv); newXS("Encode::JP::bootstrap", boot_Encode__JP, file);
              extern void boot_Encode__KR(pTHX_ CV* cv); newXS("Encode::KR::bootstrap", boot_Encode__KR, file);
              extern void boot_Encode__EBCDIC(pTHX_ CV* cv); newXS("Encode::EBCDIC::bootstrap", boot_Encode__EBCDIC, file);
              extern void boot_Encode__CN(pTHX_ CV* cv); newXS("Encode::CN::bootstrap", boot_Encode__CN, file);
              extern void boot_Encode__Symbol(pTHX_ CV* cv); newXS("Encode::Symbol::bootstrap", boot_Encode__Symbol, file);
              extern void boot_Encode__Byte(pTHX_ CV* cv); newXS("Encode::Byte::bootstrap", boot_Encode__Byte, file);
              extern void boot_Encode__TW(pTHX_ CV* cv); newXS("Encode::TW::bootstrap", boot_Encode__TW, file);
              extern void boot_Encode__EUCJPASCII(pTHX_ CV* cv); newXS("Encode::EUCJPASCII::bootstrap", boot_Encode__EUCJPASCII, file);
              extern void boot_Encode__JIS2K(pTHX_ CV* cv); newXS("Encode::JIS2K::bootstrap", boot_Encode__JIS2K, file);
              extern void boot_Encode__HanExtra(pTHX_ CV* cv); newXS("Encode::HanExtra::bootstrap", boot_Encode__HanExtra, file);
              extern void boot_Compress__Raw__Zlib(pTHX_ CV* cv); newXS("Compress::Raw::Zlib::bootstrap", boot_Compress__Raw__Zlib, file);
              extern void boot_Compress__Raw__Bzip2(pTHX_ CV* cv); newXS("Compress::Raw::Bzip2::bootstrap", boot_Compress__Raw__Bzip2, file);
              extern void boot_MIME__Base64(pTHX_ CV* cv); newXS("MIME::Base64::bootstrap", boot_MIME__Base64, file);
              extern void boot_Cwd(pTHX_ CV* cv); newXS("Cwd::bootstrap", boot_Cwd, file);
              extern void boot_Storable(pTHX_ CV* cv); newXS("Storable::bootstrap", boot_Storable, file);
              extern void boot_List__Util(pTHX_ CV* cv); newXS("List::Util::bootstrap", boot_List__Util, file);
              //extern void boot_List__SomeUtils(pTHX_ CV* cv); newXS("List::SomeUtils::bootstrap", boot_List__SomeUtils, file);
              //extern void boot_List__MoreUtils(pTHX_ CV* cv); newXS("List::MoreUtils::bootstrap", boot_List__MoreUtils, file);
              extern void boot_Fcntl(pTHX_ CV* cv); newXS("Fcntl::bootstrap", boot_Fcntl, file);
              extern void boot_Opcode(pTHX_ CV* cv); newXS("Opcode::bootstrap", boot_Opcode, file);
              
              extern void boot_DateTime(pTHX_ CV* cv); newXS("DateTime::bootstrap", boot_DateTime, file);
              extern void boot_Clone(pTHX_ CV* cv); newXS("Clone::bootstrap", boot_Clone, file);
              extern void boot_autovivification(pTHX_ CV* cv); newXS("autovivification::bootstrap", boot_autovivification, file);
              extern void boot_PadWalker(pTHX_ CV* cv); newXS("PadWalker::bootstrap", boot_PadWalker, file);
          
              extern void boot_Devel__Caller(pTHX_ CV* cv); newXS("Devel::Caller::bootstrap", boot_Devel__Caller, file);
              extern void boot_Devel__LexAlias(pTHX_ CV* cv); newXS("Devel::LexAlias::bootstrap", boot_Devel__LexAlias, file);
              extern void boot_Params__Util(pTHX_ CV* cv); newXS("Params::Util::bootstrap", boot_Params__Util, file);
              extern void boot_HTML__Parser(pTHX_ CV* cv); newXS("HTML::Parser::bootstrap", boot_HTML__Parser, file);
              extern void boot_Class__XSAccessor(pTHX_ CV* cv); newXS("Class::XSAccessor::bootstrap", boot_Class__XSAccessor, file);
              extern void boot_Sort__Key(pTHX_ CV* cv); newXS("Sort::Key::bootstrap", boot_Sort__Key, file);
              extern void boot_Variable__Magic(pTHX_ CV* cv); newXS("Variable::Magic::bootstrap", boot_Variable__Magic, file);
          
              extern void boot_XML__LibXML(pTHX_ CV* cv); newXS("XML::LibXML::bootstrap", boot_XML__LibXML, file);
              extern void boot_XML__LibXSLT(pTHX_ CV* cv); newXS("XML::LibXSLT::bootstrap", boot_XML__LibXSLT, file);
              extern void boot_XML__Parser__Expat(pTHX_ CV* cv); newXS("XML::Parser::Expat::bootstrap", boot_XML__Parser__Expat, file); 
          
              extern void boot_Text__BibTeX(pTHX_ CV* cv); newXS("Text::BibTeX::bootstrap", boot_Text__BibTeX, file);
              // extern void boot_Text__CSV_XS(pTHX_ CV* cv); newXS("Text::CSV_XS::bootstrap", boot_Text__CSV_XS, file);
              // extern void boot_DBI(pTHX_ CV* cv); newXS("DBI::bootstrap", boot_DBI, file);
              // extern void boot_DBD__SQLite(pTHX_ CV* cv); newXS("DBD::SQLite::bootstrap", boot_DBD__SQLite, file);
              // extern void boot_Net__SSLeay(pTHX_ CV* cv); newXS("Net::SSLeay::bootstrap", boot_Net__SSLeay, file);
              // extern void boot_Package__Stash__XS(pTHX_ CV* cv); newXS("Package::Stash::XS::bootstrap", boot_Package__Stash__XS, file); 
              // extern void boot_Params__Validate__XS(pTHX_ CV* cv); newXS("Params::Validate::XS::bootstrap", boot_Params__Validate__XS, file); 
              // extern void boot_Filter__Util__Call(pTHX_ CV* cv); newXS("Filter::Util::Call::bootstrap", boot_Filter__Util__Call, file); 
              // extern void boot_IO__Compress__Brotli(pTHX_ CV* cv); newXS("IO::Compress::Brotli::bootstrap", boot_IO__Compress__Brotli, file); 
          }
          
          int main(int argc, char *argv[], char* envp[])
          {
              if(argc < 1)
                  return -1;
              
              static char bin_biber[packfs_filepath_max_len];
              static char* myperl_argv[128];
              strcpy(bin_biber, packfs_builtin_prefix);
              strcat(bin_biber, "/bin/biber");
              myperl_argv[0] = argv[0];
              myperl_argv[1] = bin_biber;
              for(int i = 1; i < argc; i++) myperl_argv[1 + i] = argv[i];
              int myperl_argc = argc + 1;
              
              PERL_SYS_INIT3(&argc, &argv, &envp);
              PerlInterpreter* myperl = perl_alloc();
              if(myperl == NULL)
                  return -1;
              perl_construct(myperl);
              PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
          
              int res = perl_parse(myperl, xs_init, myperl_argc, myperl_argv, envp);
              if(res == 0)
                  res = perl_run(myperl); // error if res != 0 (or stricter in case exit(0) was called from perl code): (res & 0xFF) != 0: 
          
              PL_perl_destruct_level = 0;
              res = perl_destruct(myperl);
              perl_free(myperl);
              PERL_SYS_TERM();
          
              return res;
          }
          EOF
      
      - name: Install Perl static
        run: |
          export PERLINSTALLPREFIX=$RUNNER_WORKSPACE/busytex/packfs
          export PERLPREFIX=$PERLINSTALLPREFIX
          #export PERLPREFIX=/__w/busytex/busytex/packfs
          export LD_LIBRARY_PATH=$PERLINSTALLPREFIX/lib:$LD_LIBRARY_PATH

          mkdir perlsourcestatic && curl -L $URLPERL | tar -xzf - --strip-components=1 --directory=perlsourcestatic
          export BUILD_ZLIB=0
          cd perlsourcestatic && sh +x ./Configure -sde -Dman1dir=none -Dman3dir=none -Dprefix="$PERLPREFIX" -Dinstallprefix="$PERLINSTALLPREFIX" -Dusedevel -Uversiononly -Dlibs="-lpthread -ldl -lm -lutil -lc /lib/libz.a" -Dstatic_ext="mro Devel/Peek File/DosGlob File/Glob Sys/Syslog Sys/Hostname PerlIO/via PerlIO/mmap PerlIO/encoding B attributes Unicode/Normalize Unicode/Collate threads threads/shared IPC/SysV re Digest/MD5 Digest/SHA SDBM_File Math/BigInt/FastCalc Data/Dumper I18N/Langinfo Time/HiRes Time/Piece IO Socket Hash/Util/FieldHash Hash/Util Filter/Util/Call POSIX Encode/Unicode Encode Encode/JP Encode/KR Encode/EBCDIC Encode/CN Encode/Symbol Encode/Byte Encode/TW Compress/Raw/Zlib Compress/Raw/Bzip2 MIME/Base64 Cwd Storable List/Util Fcntl Opcode" && cd ..
          make -C perlsourcestatic
          make -C perlsourcestatic install
          echo BEFORELDD && ldd $PERLINSTALLPREFIX/bin/perl
          
          $PERLINSTALLPREFIX/bin/perl $PERLINSTALLPREFIX/bin/cpan -T Alien::Base::Wrapper Alien::Build Alien::Build::MM Alien::cmake3 Alien::Libxml2 inc::Module::Install Module::Implementation Config::AutoConf ExtUtils::LibBuilder DBI  Data::Compare Data::Dump Data::Uniqid DateTime::Calendar::Julian DateTime::Format::Builder IO::String Lingua::Translit Parse::RecDescent Regexp::Common Text::Roman Class::Accessor List::AllUtils LWP::Protocol::https Business::ISBN Business::ISMN Mozilla::CA XML::SAX::Exception MIME::Charset Business::ISSN
          # XML::Writer not installed, unneded: SDBM_File Time/Piece Storable

          printf "o conf makepl_arg LINKTYPE=static\nnotest force install %s\n" Sort::Key Encode::EUCJPASCII Encode::JIS2K Encode::HanExtra autovivification Devel::Caller Devel::LexAlias List::MoreUtils::XS List::SomeUtils::XS Clone PadWalker DateTime HTML::Parser Unicode::LineBreak Variable::Magic Log::Log4perl  Log::Log4perl::DateFormat Class::XSAccessor Package::Stash::XS Params::Util Text::CSV_XS Text::CSV Net::SSLeay DBI DBD::SQLite XML::LibXML XML::LibXSLT XML::Parser::Expat XML::LibXML::Simple XML::Writer PerlIO::utf8_strict Sub::Identify Storable | $PERLINSTALLPREFIX/bin/perl $PERLINSTALLPREFIX/bin/cpan 
          
          mkdir -p myext/URL_Params_Validate_XS && curl -L $URL_Params_Validate_XS | tar -xzf - --strip-components=1 --directory myext/URL_Params_Validate_XS && cd myext/URL_Params_Validate_XS
          $PERLINSTALLPREFIX/bin/perl ./Build.PL && $PERLINSTALLPREFIX/bin/perl ./Build && $PERLINSTALLPREFIX/bin/perl ./Build install
          ar crs $PERLINSTALLPREFIX/lib/perl5/site_perl/$PERLVER/$PERLPLATFORM/auto/Params/Validate/XS/XS.a lib/Params/Validate/XS.o
          cd ../..
          mkdir -p myext/URL_Text_BibTeX && curl -L $URL_Text_BibTeX | tar -xzf - --strip-components=1 --directory myext/URL_Text_BibTeX && cd myext/URL_Text_BibTeX
          $PERLINSTALLPREFIX/bin/perl ./Build.PL && $PERLINSTALLPREFIX/bin/perl ./Build && $PERLINSTALLPREFIX/bin/perl ./Build install
          ar crs $PERLINSTALLPREFIX/lib/libbtparse.a btparse/src/init.o btparse/src/input.o btparse/src/bibtex.o btparse/src/err.o btparse/src/scan.o btparse/src/error.o btparse/src/lex_auxiliary.o btparse/src/parse_auxiliary.o btparse/src/bibtex_ast.o btparse/src/sym.o btparse/src/util.o btparse/src/postprocess.o btparse/src/macros.o btparse/src/traversal.o btparse/src/modify.o btparse/src/names.o btparse/src/tex_tree.o btparse/src/string_util.o btparse/src/format_name.o
          ar crs $PERLINSTALLPREFIX/lib/perl5/site_perl/$PERLVER/$PERLPLATFORM/auto/Text/BibTeX/BibTeX.a xscode/BibTeX.o xscode/btxs_support.o
          cd ../..
          mkdir -p myext/URL_IO_Compress_Brotli && curl -L $URL_IO_Compress_Brotli | tar -xzf - --strip-components=1 --directory myext/URL_IO_Compress_Brotli && cd myext/URL_IO_Compress_Brotli
          $PERLINSTALLPREFIX/bin/perl Makefile.PL LINKTYPE=static
          # https://github.com/timlegge/perl-IO-Compress-Brotli/issues/5
          sed -i 's/$(CP) $(MYEXTLIB) "$@"/$(CP) $(MYEXTLIB) "$(dir $@)"/' Makefile
          make && make install
          cd ../..
          mkdir -p myext/URL_Biber && curl -L $URL_Biber | tar -xzf - --strip-components=1 --directory myext/URL_Biber && cd myext/URL_Biber
          $PERLINSTALLPREFIX/bin/perl ./Build.PL && $PERLINSTALLPREFIX/bin/perl ./Build && $PERLINSTALLPREFIX/bin/perl ./Build install
          cd ../..

          ucpath="$PERLINSTALLPREFIX/lib/perl5/$PERLVER/Unicode/Collate"
          mkdir biber && curl -L $URL_Biber | tar -xzf - --strip-components=1 --directory=biber
          mkdir -p packfs/lib/Biber packfs/lib/Biber/LaTeX packfs/lib/Unicode/Collate packfs/lib/Mozilla/CA packfs/lib/auto packfs/lib/Business/ISBN
          cp biber/data/biber-tool.conf biber/data/schemata/config.rnc biber/data/schemata/config.rng biber/data/schemata/bcf.rnc biber/data/schemata/bcf.rng biber/data/bcf.xsl packfs/lib/Biber
          cp biber/lib/Biber/LaTeX/recode_data.xml packfs/lib/Biber/LaTeX
          
          cp -r $ucpath/Locale $ucpath/CJK $ucpath/allkeys.txt $ucpath/keys.txt packfs/lib/Unicode/Collate
          cp $PERLINSTALLPREFIX/lib/perl5/site_perl/$PERLVER/Mozilla/CA/cacert.pem packfs/lib/Mozilla/CA/cacert.pem
          cp -r $PERLINSTALLPREFIX/lib/perl5/$PERLVER/$PERLPLATFORM/PerlIO packfs/lib/PerlIO
          cp -r $PERLINSTALLPREFIX/lib/perl5/$PERLVER/$PERLPLATFORM/auto/PerlIO packfs/lib/auto/PerlIO
          cp $PERLINSTALLPREFIX/lib/perl5/site_perl/$PERLVER/Business/ISBN/RangeMessage.xml packfs/lib/Business/ISBN/RangeMessage.xml

          $PERLINSTALLPREFIX/bin/perl perlpack.pl -i packfs -o perlpack.h --prefix=$PERLPREFIX --ld=ld --exclude '\.a$|\.so$|\.pod$|\.ld$|\.h$|bin\/' --include 'bin/biber'
          cc -o busybiber busybiber.c -DPACKFS_BUILTIN_PREFIX=$PERLPREFIX  -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I$PWD/perlsourcestatic -I/usr/local/include   -Wl,-E -fstack-protector-strong -fwrapv -fno-strict-aliasing -L/usr/local/lib perlsourcestatic/libperl.a -lc -lpthread -ldl -lm -lutil   --static -static -static-libstdc++ -static-libgcc  -Wl,--wrap=open,--wrap=close,--wrap=read,--wrap=access,--wrap=lseek,--wrap=stat,--wrap=fstat,--wrap=fopen,--wrap=fileno @perlpack.h.txt \
              $(printf "$PERLINSTALLPREFIX/lib/perl5/site_perl/$PERLVER/$PERLPLATFORM/auto/%s " Class/XSAccessor/XSAccessor.a Params/Util/Util.a Params/Validate/XS/XS.a XML/LibXML/LibXML.a XML/Parser/Expat/Expat.a XML/LibXSLT/LibXSLT.a DateTime/DateTime.a Text/CSV_XS/CSV_XS.a Text/BibTeX/BibTeX.a Variable/Magic/Magic.a IO/Compress/Brotli/Brotli.a IO/Compress/Brotli/libbrotlidec.a IO/Compress/Brotli/libbrotlienc.a IO/Compress/Brotli/libbrotlicommon.a HTML/Parser/Parser.a Package/Stash/XS/XS.a PadWalker/PadWalker.a Net/SSLeay/SSLeay.a List/SomeUtils/XS/XS.a List/MoreUtils/XS/XS.a Unicode/LineBreak/LineBreak.a Devel/LexAlias/LexAlias.a Devel/Caller/Caller.a Storable/Storable.a autovivification/autovivification.a DBD/SQLite/SQLite.a DBI/DBI.a Sub/Identify/Identify.a Sort/Key/Key.a PerlIO/utf8_strict/utf8_strict.a Clone/Clone.a Encode/JIS2K/JIS2K.a Encode/HanExtra/HanExtra.a Encode/EUCJPASCII/EUCJPASCII.a) \
              $(printf "$PERLINSTALLPREFIX/lib/perl5/$PERLVER/$PERLPLATFORM/auto/%s " mro/mro.a File/Glob/Glob.a File/DosGlob/DosGlob.a I18N/Langinfo/Langinfo.a SDBM_File/SDBM_File.a Compress/Raw/Zlib/Zlib.a Compress/Raw/Bzip2/Bzip2.a POSIX/POSIX.a Fcntl/Fcntl.a Data/Dumper/Dumper.a Math/BigInt/FastCalc/FastCalc.a IO/IO.a Opcode/Opcode.a Socket/Socket.a threads/shared/shared.a threads/threads.a MIME/Base64/Base64.a Time/HiRes/HiRes.a Time/Piece/Piece.a Filter/Util/Call/Call.a List/Util/Util.a Unicode/Collate/Collate.a Unicode/Normalize/Normalize.a B/B.a Devel/Peek/Peek.a Storable/Storable.a attributes/attributes.a re/re.a Hash/Util/Util.a Hash/Util/FieldHash/FieldHash.a IPC/SysV/SysV.a PerlIO/encoding/encoding.a PerlIO/mmap/mmap.a PerlIO/via/via.a Sys/Hostname/Hostname.a Sys/Syslog/Syslog.a Encode/KR/KR.a Encode/JP/JP.a Encode/Byte/Byte.a Encode/CN/CN.a Encode/Encode.a Encode/Unicode/Unicode.a Encode/Symbol/Symbol.a Encode/EBCDIC/EBCDIC.a Encode/TW/TW.a Cwd/Cwd.a Digest/MD5/MD5.a Digest/SHA/SHA.a) \
              $PERLINSTALLPREFIX/lib/libbtparse.a \
              $PERLINSTALLPREFIX/lib/perl5/$PERLVER/$PERLPLATFORM/CORE/libperl.a \
              /usr/lib/libxslt.a \
              /usr/lib/libexslt.a \
              /usr/lib/libexpat.a \
              /usr/lib/libcrypto.a /usr/lib/libgcrypt.a /usr/lib/libgpg-error.a \
              /usr/lib/libxml2.a \
              /lib/libz.a \
              /usr/lib/liblzma.a \
              /usr/lib/libssl.a
          ./busybiber --help
          cd biber/testfiles && ../../busybiber --validate-datamodel --convert-control test && test -f test.bcf.html

      - name: Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: staticbiber
          path: |
            perlpack.h
            busybiber
            biber/testfiles/

@vadimkantorov
Copy link
Author

vadimkantorov commented Jan 18, 2025

I forgot to delete it ...

@plk might be nice to also publish the binaries in GitHub Releases? The GitHub UI is less cluttered than SourceForge

@plk
Copy link
Owner

plk commented Jan 18, 2025

Yes, I've looked at this but it wasn't what we needed with certain static links that are used to package for TexLive.

@vadimkantorov
Copy link
Author

vadimkantorov commented Jan 18, 2025

GitHub releases artifact links work fairly well (and are static), but we need to use curl -L to follow redirects... At least for non-texlive uses it could be useful to have the binaries also on GitHub releases - especially if you add the GitHub Actions workflow for the build/test

@plk
Copy link
Owner

plk commented Jan 18, 2025

I remember there was something about having folder structures like we do on SF - that was an issue.

@vadimkantorov
Copy link
Author

vadimkantorov commented Jan 18, 2025

I guess another viable and simple enough option is to host these on GitHub Pages - you can push any binary files/artifacts with an arbitrary folder structure to a GH Pages repo (also in an automated way from a GH Action)...

@plk
Copy link
Owner

plk commented Jan 18, 2025

I might look at it but I have a lot of automation I would need to change ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants