embed ledger binary inside paisa
This commit is contained in:
parent
3a740952a6
commit
27165ea4a4
|
@ -9,9 +9,12 @@ on:
|
|||
|
||||
jobs:
|
||||
linux-binary:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2.4.0
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
@ -22,6 +25,8 @@ jobs:
|
|||
sudo apt-get install -y sqlite3
|
||||
npm install
|
||||
npm run build
|
||||
nix-build ledger.nix
|
||||
cp ./result/bin/ledger internal/binary/ledger
|
||||
go build
|
||||
cp paisa paisa-linux-amd64
|
||||
- name: Release
|
||||
|
@ -33,11 +38,13 @@ jobs:
|
|||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
mac-binary:
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-11
|
||||
steps:
|
||||
- uses: actions/checkout@v2.4.0
|
||||
- run: |
|
||||
brew install --force --overwrite go sqlite3
|
||||
brew install --build-from-source --verbose ./ledger.rb
|
||||
cp "$(brew --prefix ledger)/bin/ledger" internal/binary/ledger
|
||||
npm install
|
||||
npm run build
|
||||
go build
|
||||
|
@ -51,7 +58,7 @@ jobs:
|
|||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
windows-binary:
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- uses: actions/checkout@v2.4.0
|
||||
- uses: actions/setup-node@v3
|
||||
|
@ -62,8 +69,11 @@ jobs:
|
|||
go-version: '1.20.5'
|
||||
- run: |
|
||||
choco install sqlite
|
||||
choco install wget
|
||||
npm install
|
||||
npm run build
|
||||
wget https://github.com/FullofQuarks/Windows-Ledger-Binaries/releases/download/v3.3.2/ledger.exe
|
||||
cp ledger.exe internal/binary/ledger
|
||||
go build
|
||||
cp paisa.exe paisa-windows-amd64.exe
|
||||
- name: Release
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var cachedLedgerBinaryPath string
|
||||
|
||||
func LedgerBinaryPath() string {
|
||||
if cachedLedgerBinaryPath != "" {
|
||||
return cachedLedgerBinaryPath
|
||||
}
|
||||
|
||||
path, err := exec.LookPath("ledger")
|
||||
if err == nil {
|
||||
cachedLedgerBinaryPath = path
|
||||
return path
|
||||
}
|
||||
|
||||
cacheDir, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
binDir := filepath.Join(cacheDir, "paisa")
|
||||
binaryPath := "ledger"
|
||||
if runtime.GOOS == "windows" {
|
||||
binaryPath += ".exe"
|
||||
}
|
||||
|
||||
path = filepath.Join(binDir, binaryPath)
|
||||
err = stage(path, ledgerBinary, 0750)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
cachedLedgerBinaryPath = path
|
||||
return path
|
||||
}
|
||||
|
||||
//go:embed ledger
|
||||
var ledgerBinary []byte
|
||||
|
||||
// EmbeddedBinaryNeedsUpdate returns true if the provided embedded binary file should
|
||||
// be updated. This determination is based on the modification times and file sizes of both
|
||||
// the provided executable and the embedded executable. It is expected that the embedded binary
|
||||
// modification times should match the main `paisa` executable.
|
||||
func embeddedBinaryNeedsUpdate(exinfo os.FileInfo, embeddedBinaryPath string, size int64) bool {
|
||||
if pathinfo, err := os.Stat(embeddedBinaryPath); err == nil {
|
||||
return !exinfo.ModTime().Equal(pathinfo.ModTime()) || pathinfo.Size() != size
|
||||
}
|
||||
|
||||
// If the stat fails, the file is either missing or permissions are missing
|
||||
// to read this -- let above know that an update should be attempted.
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Stage ...
|
||||
func stage(p string, binData []byte, filemode os.FileMode) error {
|
||||
log.Debugf("Staging '%s'", p)
|
||||
|
||||
err := os.MkdirAll(filepath.Dir(p), filemode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create dir '%s': %w", filepath.Dir(p), err)
|
||||
}
|
||||
|
||||
selfexe, err := os.Executable()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to determine current executable: %w", err)
|
||||
}
|
||||
|
||||
exinfo, err := os.Stat(selfexe)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to stat '%s': %w", selfexe, err)
|
||||
}
|
||||
|
||||
if !embeddedBinaryNeedsUpdate(exinfo, p, int64(len(binData))) {
|
||||
log.Debug("Re-use existing file:", p)
|
||||
return nil
|
||||
}
|
||||
|
||||
infile, err := os.Open(selfexe)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to open executable '%s': %w", selfexe, err)
|
||||
}
|
||||
defer infile.Close()
|
||||
|
||||
log.Debugf("Writing static file: '%s'", p)
|
||||
|
||||
_ = os.Remove(p)
|
||||
err = os.WriteFile(p, binData, 0550)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to copy to '%s': %w", p, err)
|
||||
}
|
||||
|
||||
// In order to properly determine if an update of an embedded binary file is needed,
|
||||
// the staged embedded binary needs to have the same modification time as the `paisa`
|
||||
// executable.
|
||||
if err := os.Chtimes(p, exinfo.ModTime(), exinfo.ModTime()); err != nil {
|
||||
return fmt.Errorf("failed to set file modification times of '%s': %w", p, err)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
// DUMMY FILE THAT WILL GET REPLACED DURING RELEASE
|
|
@ -20,6 +20,7 @@ import (
|
|||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ananthakumaran/paisa/internal/binary"
|
||||
"github.com/ananthakumaran/paisa/internal/config"
|
||||
"github.com/ananthakumaran/paisa/internal/model/posting"
|
||||
"github.com/ananthakumaran/paisa/internal/model/price"
|
||||
|
@ -52,16 +53,12 @@ func Cli() Ledger {
|
|||
|
||||
func (LedgerCLI) ValidateFile(journalPath string) ([]LedgerFileError, string, error) {
|
||||
errors := []LedgerFileError{}
|
||||
_, err := exec.LookPath("ledger")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
command := exec.Command("ledger", "-f", journalPath, "balance")
|
||||
command := exec.Command(binary.LedgerBinaryPath(), "-f", journalPath, "balance")
|
||||
var output, error bytes.Buffer
|
||||
command.Stdout = &output
|
||||
command.Stderr = &error
|
||||
err = command.Run()
|
||||
err := command.Run()
|
||||
if err == nil {
|
||||
return errors, output.String(), nil
|
||||
}
|
||||
|
@ -80,12 +77,7 @@ func (LedgerCLI) ValidateFile(journalPath string) ([]LedgerFileError, string, er
|
|||
func (LedgerCLI) Parse(journalPath string, _prices []price.Price) ([]*posting.Posting, error) {
|
||||
var postings []*posting.Posting
|
||||
|
||||
_, err := exec.LookPath("ledger")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
postings, err = execLedgerCommand(journalPath, []string{})
|
||||
postings, err := execLedgerCommand(journalPath, []string{})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -106,16 +98,11 @@ func (LedgerCLI) Parse(journalPath string, _prices []price.Price) ([]*posting.Po
|
|||
func (LedgerCLI) Prices(journalPath string) ([]price.Price, error) {
|
||||
var prices []price.Price
|
||||
|
||||
_, err := exec.LookPath("ledger")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
command := exec.Command("ledger", "-f", journalPath, "pricesdb")
|
||||
command := exec.Command(binary.LedgerBinaryPath(), "-f", journalPath, "pricesdb")
|
||||
var output, error bytes.Buffer
|
||||
command.Stdout = &output
|
||||
command.Stderr = &error
|
||||
err = command.Run()
|
||||
err := command.Run()
|
||||
if err != nil {
|
||||
return prices, err
|
||||
}
|
||||
|
@ -280,7 +267,7 @@ func execLedgerCommand(journalPath string, flags []string) ([]*posting.Posting,
|
|||
|
||||
args := append(append([]string{"-f", journalPath}, flags...), "csv", "--csv-format", "%(quoted(date)),%(quoted(payee)),%(quoted(display_account)),%(quoted(commodity(scrub(display_amount)))),%(quoted(quantity(scrub(display_amount)))),%(quoted(scrub(market(amount,date,'"+config.DefaultCurrency()+"') * 100000000))),%(quoted(xact.filename)),%(quoted(xact.id)),%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\"))),%(quoted(tag('Recurring'))),%(quoted(xact.beg_line)),%(quoted(xact.end_line))\n")
|
||||
|
||||
command := exec.Command("ledger", args...)
|
||||
command := exec.Command(binary.LedgerBinaryPath(), args...)
|
||||
var output, error bytes.Buffer
|
||||
command.Stdout = &output
|
||||
command.Stderr = &error
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
{ pkgs ? import <nixpkgs> { } }:
|
||||
|
||||
with pkgs;
|
||||
|
||||
pkgsStatic.stdenv.mkDerivation {
|
||||
pname = "ledger";
|
||||
version = "3.3.2";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "ledger";
|
||||
repo = "ledger";
|
||||
rev = "4355c4faf157d5ef47b126286aa501742732708d";
|
||||
hash = "sha256-9IowdrQpJarALr21Y+Mhmld+eC4YUpEn+goqWyBb6Xc=";
|
||||
};
|
||||
|
||||
outputs = [ "out" "dev" ];
|
||||
|
||||
buildInputs = [ pkgsStatic.gmp pkgsStatic.mpfr gnused pkgsStatic.boost ];
|
||||
|
||||
nativeBuildInputs = [ cmake tzdata ];
|
||||
|
||||
cmakeFlags = [
|
||||
"-DCMAKE_INSTALL_LIBDIR=lib"
|
||||
"-DBUILD_DOCS:BOOL=OFF"
|
||||
"-DUSE_PYTHON:BOOL=OFF"
|
||||
"-DUSE_GPGME:BOOL=OFF"
|
||||
"-DBUILD_LIBRARY:BOOL=OFF"
|
||||
];
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
installTargets = [ "install" ];
|
||||
|
||||
checkPhase = ''
|
||||
runHook preCheck
|
||||
env LD_LIBRARY_PATH=$PWD \
|
||||
DYLD_LIBRARY_PATH=$PWD \
|
||||
ctest -j$NIX_BUILD_CORES
|
||||
runHook postCheck
|
||||
'';
|
||||
|
||||
doCheck = true;
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
class Ledger < Formula
|
||||
desc "Command-line, double-entry accounting tool"
|
||||
homepage "https://ledger-cli.org/"
|
||||
url "https://github.com/ledger/ledger/archive/v3.3.2.tar.gz"
|
||||
sha256 "555296ee1e870ff04e2356676977dcf55ebab5ad79126667bc56464cb1142035"
|
||||
license "BSD-3-Clause"
|
||||
revision 1
|
||||
head "https://github.com/ledger/ledger.git", branch: "master"
|
||||
|
||||
livecheck do
|
||||
url :stable
|
||||
regex(/^v?(\d+(?:\.\d+)+)$/i)
|
||||
end
|
||||
|
||||
depends_on "boost"
|
||||
depends_on "gmp"
|
||||
depends_on "mpfr"
|
||||
depends_on "python@3.11" => :build
|
||||
depends_on "cmake" => :build
|
||||
|
||||
patch :DATA
|
||||
|
||||
def install
|
||||
ENV.cxx11
|
||||
ENV.prepend_path "PATH", Formula["python@3.11"].opt_libexec/"bin"
|
||||
|
||||
args = %W[
|
||||
--jobs=#{ENV.make_jobs}
|
||||
--output=build
|
||||
--prefix=#{prefix}
|
||||
--boost=#{Formula["boost"].opt_prefix}
|
||||
--
|
||||
-DCMAKE_FIND_LIBRARY_SUFFIXES=.a
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF
|
||||
-DBUILD_DOCS:BOOL=OFF
|
||||
-DBoost_NO_BOOST_CMAKE=ON
|
||||
-DUSE_PYTHON:BOOL=OFF
|
||||
-DUSE_GPGME:BOOL=OFF
|
||||
-DBUILD_LIBRARY:BOOL=OFF
|
||||
-DBoost_USE_STATIC_LIBS:BOOL=ON
|
||||
] + std_cmake_args
|
||||
system "./acprep", "make", *args
|
||||
system "./acprep", "make", "install", *args
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
__END__
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 83a6f89d..e84be891 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -274,8 +274,8 @@ find_opt_library_and_header(EDIT_PATH histedit.h EDIT_LIB edit HAVE_EDIT)
|
||||
########################################################################
|
||||
|
||||
macro(add_ledger_library_dependencies _target)
|
||||
- target_link_libraries(${_target} ${MPFR_LIB})
|
||||
- target_link_libraries(${_target} ${GMP_LIB})
|
||||
+ target_link_libraries(${_target} /usr/local/lib/libmpfr.a)
|
||||
+ target_link_libraries(${_target} /usr/local/lib/libgmp.a)
|
||||
if (HAVE_EDIT)
|
||||
target_link_libraries(${_target} ${EDIT_LIB})
|
||||
endif()
|
Loading…
Reference in New Issue