From 6a0c8255851926b87379ca6b1e62bd78e631db37 Mon Sep 17 00:00:00 2001 From: Charles McGarvey Date: Mon, 2 Sep 2019 19:37:33 -0600 Subject: [PATCH] add fatpacker script --- bin/homebank2ledger | 45 ++++++++++- dist.ini | 10 ++- maint/branch_solo.pl | 62 +++++++++++++++ maint/fatpack.pl | 180 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 291 insertions(+), 6 deletions(-) create mode 100755 maint/branch_solo.pl create mode 100755 maint/fatpack.pl diff --git a/bin/homebank2ledger b/bin/homebank2ledger index 1139928..03d710f 100644 --- a/bin/homebank2ledger +++ b/bin/homebank2ledger @@ -13,13 +13,13 @@ =head1 DESCRIPTION -C converts L files to a format usable by +F converts L files to a format usable by L. It can also convert directly to the similar L format. This software is B, in early development. Its interface may change without notice. -I wrote C because I have been maintaining my own personal finances using HomeBank +I wrote F because I have been maintaining my own personal finances using HomeBank (which is awesome) and I wanted to investigate using plain text accounting programs. It works well enough for my data, but you may be using HomeBank features that I don't so there may be cases this doesn't handle well or at all. Feel free to file a bug report. This script does NOT try to modify @@ -45,6 +45,43 @@ You can migrate the data you have in HomeBank so you can start maintaining your Or if you don't plan to switch completely off of HomeBank, you can continue to maintain your accounts in HomeBank and use this script to also take advantage of the reports Ledger offers. +=head1 INSTALL + +There are several ways to install F to your system. + +=head2 using cpanm + +You can install F using L. If you have a local perl (plenv, perlbrew, etc.), +you can just do: + + cpanm App::Homebank2Ledger + +to install the F executable and its dependencies. The executable will be installed +to your perl's bin path, like F<~/perl5/perlbrew/bin/homebank2ledger>. + +If you're installing to your system perl, you can do: + + cpanm --sudo App::Homebank2Ledger + +to install the F executable to a system directory, like +F (depending on your perl). + +=head2 Downloading just the executable + +You may also choose to download F as a single executable, like this: + + curl -OL https://raw.githubusercontent.com/chazmcgarvey/homebank2ledger/solo/homebank2ledger + chmod +x homebank2ledger + +=head2 For developers + +If you're a developer and want to hack on the source, clone the repository and pull the +dependencies: + + git clone https://github.com/chazmcgarvey/homebank2ledger.git + cd homebank2ledger + make bootstrap # installs dependencies; requires cpanm + =head1 OPTIONS =head2 --version @@ -135,7 +172,7 @@ Date must be in the form "YYYY-MM-DD". Defaults to the date of the first transac =head2 --rename-account STR -Specifies a mapping for renaming accounts in the output. By default C tries to come +Specifies a mapping for renaming accounts in the output. By default F tries to come up with sensible account names (based on your HomeBank accounts and categories) that fit into five root accounts: @@ -213,6 +250,8 @@ it's just plain text. =cut +# FATPACK - Do not remove this line. + use warnings; use strict; diff --git a/dist.ini b/dist.ini index 32568d3..1e774fc 100644 --- a/dist.ini +++ b/dist.ini @@ -7,9 +7,13 @@ copyright_year = 2019 license = MIT [@Filter] --bundle = @Author::CCM --remove = Test::CleanNamespaces -max_target_perl = 5.14 +-bundle = @Author::CCM +-remove = Test::CleanNamespaces +max_target_perl = 5.14 +PruneFiles.filename = maint + +[Run::Release] +run = %x maint%pbranch_solo.pl %v %d [ConsistentVersionTest] diff --git a/maint/branch_solo.pl b/maint/branch_solo.pl new file mode 100755 index 0000000..c9eed65 --- /dev/null +++ b/maint/branch_solo.pl @@ -0,0 +1,62 @@ +#!/usr/bin/env perl + +# This script prepares the homebank2ledger script for standalone use and puts it in a new branch. + +use strict; +use warnings; +use autodie; + +use File::Copy; +use File::Path qw(make_path remove_tree); +use String::ShellQuote; + + +my $version = shift or die 'Need version'; +my $distdir = shift or die 'Need distdir'; + +my $branch_name = 'solo'; +my $script_name = 'homebank2ledger'; + +my $branch_oldref = ''; +my $branch_oldref_origin = ''; + +open(my $fh, '-|', qw{git show-ref}, $branch_name); +while (my $line = <$fh>) { + chomp $line; + my ($hash, $ref) = split(/\s+/, $line); + $branch_oldref = $hash if $ref eq "refs/heads/$branch_name"; + $branch_oldref_origin = $hash if $ref eq "refs/remotes/origin/$branch_name"; +} +if ($branch_oldref_origin && $branch_oldref ne $branch_oldref_origin) { + # reset local branch + system(qw{git branch -f}, $branch_name, "origin/$branch_name"); + $branch_oldref = $branch_oldref_origin +} + +my $commit_msg = shell_quote("Release $version"); + +my $solodir = "solo_branch.$$"; +make_path($solodir); + +use Config; +system($Config{'perlpath'}, qw{maint/fatpack.pl}, $distdir); +move($script_name, "$solodir/$script_name"); + +copy("$distdir/README", "$solodir/README"); + +system(qw{git update-index --add}, glob("$solodir/*")); +my $tree_ref = `git write-tree --prefix=$solodir/`; +chomp $tree_ref; + +system(qw{git reset}); +remove_tree($solodir); + +my $branch_oldref_safe = shell_quote($branch_oldref); +my $tree_ref_safe = shell_quote($tree_ref); +my $parent = $branch_oldref ? "-p $branch_oldref_safe" : ''; +my $commit_ref = `git commit-tree -m $commit_msg $parent $tree_ref_safe`; +chomp $commit_ref; + +system(qw(git branch -f), $branch_name, $commit_ref); +system(qw(git tag -a -m), "Version $version", "$branch_name-$version", $commit_ref); + diff --git a/maint/fatpack.pl b/maint/fatpack.pl new file mode 100755 index 0000000..af14d1b --- /dev/null +++ b/maint/fatpack.pl @@ -0,0 +1,180 @@ +#!/usr/bin/env perl + +# This script creates a fatpacked version of homebank2ledger. Much of this code was inspired by or +# blatantly copied from cpanminus build scripts, written by Tatsuhiko Miyagawa. + +use warnings FATAL => 'all'; +use strict; +use autodie ':all'; + +use App::FatPacker (); +use Cwd; +use File::Find; +use File::Path; +use File::pushd; +use Module::CoreList; + + +my $distdir = shift; + +my $script_name = 'bin/homebank2ledger'; +my $libdir = 'lib'; + +if ($distdir) { + if (-d "$distdir/blib") { + $script_name = "$distdir/blib/script/homebank2ledger"; + $libdir = "$distdir/blib/lib"; + } + else { + $script_name = "$distdir/$script_name"; + $libdir = "$distdir/$libdir"; + } +} + +make_fatlib(); +make_script(); +exit; + + +BEGIN { + # IO::Socket::IP requires newer Socket, which is C-based + $ENV{PERL_HTTP_TINY_IPV4_ONLY} = 1; +} + +END { + no autodie; + unlink('homebank2ledger.tmp'); + rmtree('.fatpack-build'); + rmtree('fatlib'); +} + + +sub find_requires { + my $file = shift; + + my %requires; + open my $in, "<", $file; + while (<$in>) { + /^\s*(?:use|require) (\S+)[^;]*;\s*$/ + and $requires{$1} = 1; + } + + keys %requires; +} + +sub mod_to_pm { + local $_ = shift; + s!::!/!g; + "$_.pm"; +} + +sub pm_to_mod { + local $_ = shift; + s!/!::!g; + s/\.pm$//; + $_; +} + +sub in_lib { + my $file = shift; + -e "$libdir/$file"; +} + +sub is_core { + my $module = shift; + exists $Module::CoreList::version{5.008001}{$module}; +} + +sub exclude_modules { + my($modules, $except) = @_; + my %exclude = map { $_ => 1 } @$except; + [ grep !$exclude{$_}, @$modules ]; +} + +sub pack_modules { + my($path, $modules, $no_trace) = @_; + + $modules = exclude_modules($modules, $no_trace); + + my $packer = App::FatPacker->new; + my @requires = grep !is_core(pm_to_mod($_)), grep /\.pm$/, split /\n/, + $packer->trace(use => $modules, args => ['-e', 1]); + push @requires, map mod_to_pm($_), @$no_trace; + + my @packlists = $packer->packlists_containing(\@requires); + for my $packlist (@packlists) { + print "Packing $packlist\n"; + } + $packer->packlists_to_tree($path, \@packlists); +} + +sub make_fatlib { + my @modules = grep !in_lib(mod_to_pm($_)), find_requires($script_name); + + pack_modules(cwd . '/fatlib', \@modules, []); + + use Config; + print "Remove fatlib/$Config{archname}\n"; + rmtree("fatlib/$Config{archname}"); + rmtree("fatlib/POD2"); + + my $want = sub { + if (/\.pod$/) { + print "Remove $_\n"; + unlink $_; + } + }; + + find({ wanted => $want, no_chdir => 1 }, 'fatlib'); +} + + +sub generate_file { + my($base, $target, $fatpack) = @_; + + open my $in, "<", $base; + open my $out, ">", "$target.tmp"; + + print STDERR "Generating $target from $base\n"; + + while (<$in>) { + s|^#!\h*perl|#!/usr/bin/env perl|; + s|^# FATPACK.*|$fatpack|; + print $out $_; + } + + close $out; + + eval { unlink $target }; + rename "$target.tmp", $target; +} + +sub make_script { + mkdir '.fatpack-build'; + system qw(cp -r fatlib), $libdir, qw(.fatpack-build/); + + my $fatpack_compact = do { + my $dir = pushd '.fatpack-build'; + + my @files; + my $want = sub { + push @files, $_ if /\.pm$/; + if (/\.pod$/) { + print "Remove $_\n"; + unlink $_; + } + }; + + find({ wanted => $want, no_chdir => 1 }, 'fatlib', 'lib'); + system qw(perlstrip --cache -v), @files; + + `fatpack file`; + }; + + my $filename = $script_name; + $filename =~ s!^.*/!!; + + generate_file($script_name, $filename, $fatpack_compact); + chmod 0755, $filename; +} + -- 2.43.0