diff --git a/.gitignore b/.gitignore index df358dbc..a34f7dc2 100644 --- a/.gitignore +++ b/.gitignore @@ -56,8 +56,7 @@ tests/shunit2 tests-2/Gemfile.lock *-package metanorma-package -tests/test-11/tebako-test-0.0.1.gem -tests/test-15/tebako-bundle-test-0.0.1.gem +tests/**/*.gem packed-mn .package-ready extract/ diff --git a/lib/tebako/packager.rb b/lib/tebako/packager.rb index 2d2d5d64..2a6b5cb0 100644 --- a/lib/tebako/packager.rb +++ b/lib/tebako/packager.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Copyright (c) 2021-2024 [Ribose Inc](https://www.ribose.com). +# Copyright (c) 2021-2025 [Ribose Inc](https://www.ribose.com). # All rights reserved. # This file is a part of tebako # @@ -43,25 +43,29 @@ module Tebako # Tebako packaging support (internal) module Packager FILES_TO_RESTORE = %w[ - main.c + common.mk + configure + config.status dir.c dln.c file.c - io.c - tool/mkconfig.rb gem_prelude.rb - ].freeze - - FILES_TO_RESTORE_MSYS = %w[ + io.c + main.c + Makefile ruby.c - win32/file.c - ].freeze - # Do not need to restore cygwin/GNUmakefile.in - # because it is patched (differently) both on pass 1 and pass2 - # cygwin/GNUmakefile.in - - FILES_TO_RESTORE_MUSL = %w[ thread_pthread.c + util.c + ext/bigdecimal/bigdecimal.h + ext/Setup + cygwin/GNUmakefile.in + include/ruby/onigmo.h + lib/rubygems/openssl.rb + lib/rubygems/path_support.rb + template/Makefile.in + tool/mkconfig.rb + win32/winmain.c + win32/file.c ].freeze class << self @@ -118,16 +122,14 @@ def mkdwarfs(deps_bin_dir, data_bin_file, data_src_dir, descriptor = nil) # Executed before Ruby build, patching ensures that Ruby itself is linked statically def pass1(ostype, ruby_source_dir, mount_point, src_dir, ruby_ver) puts "-- Running pass1 script" - PatchHelpers.recreate(src_dir) - patch = crt_pass1_patch(ostype, mount_point, ruby_ver) - do_patch(patch.patch_map, ruby_source_dir) - # Roll back pass1a, pass2 patches + # Roll all known patches # Just in case we are recovering after some error - PatchHelpers.restore_and_save_files(FILES_TO_RESTORE, ruby_source_dir) - PatchHelpers.restore_and_save_files(FILES_TO_RESTORE_MUSL, ruby_source_dir) if ostype =~ /linux-musl/ - PatchHelpers.restore_and_save_files(FILES_TO_RESTORE_MSYS, ruby_source_dir) if ostype =~ /msys/ + PatchHelpers.restore_and_save_files(FILES_TO_RESTORE, ruby_source_dir, strict: false) + + patch = crt_pass1_patch(ostype, mount_point, ruby_ver) + do_patch(patch.patch_map, ruby_source_dir) end # Pass1A diff --git a/lib/tebako/packager/patch_helpers.rb b/lib/tebako/packager/patch_helpers.rb index f9cb12f2..081ecf79 100755 --- a/lib/tebako/packager/patch_helpers.rb +++ b/lib/tebako/packager/patch_helpers.rb @@ -91,8 +91,12 @@ def recreate(dirname) FileUtils.mkdir(dirname) end - def restore_and_save(fname) - raise Tebako::Error, "Could not save #{fname} because it does not exist." unless File.exist?(fname) + def restore_and_save(fname, strict: true) + unless File.exist?(fname) + return unless strict + + raise Tebako::Error, "Could not save #{fname} because it does not exist." + end old_fname = "#{fname}.old" if File.exist?(old_fname) @@ -102,9 +106,9 @@ def restore_and_save(fname) FileUtils.cp(fname, old_fname) end - def restore_and_save_files(files, ruby_source_dir) + def restore_and_save_files(files, ruby_source_dir, strict: true) files.each do |fname| - restore_and_save "#{ruby_source_dir}/#{fname}" + restore_and_save("#{ruby_source_dir}/#{fname}", strict: strict) end end diff --git a/lib/tebako/version.rb b/lib/tebako/version.rb index 130ada0c..8df71fee 100644 --- a/lib/tebako/version.rb +++ b/lib/tebako/version.rb @@ -26,5 +26,5 @@ # POSSIBILITY OF SUCH DAMAGE. module Tebako - VERSION = "0.12.1" + VERSION = "0.12.2" end diff --git a/spec/packager/patch_helpers_spec.rb b/spec/packager/patch_helpers_spec.rb new file mode 100644 index 00000000..c5319da4 --- /dev/null +++ b/spec/packager/patch_helpers_spec.rb @@ -0,0 +1,178 @@ +# frozen_string_literal: true + +# Copyright (c) 2025 [Ribose Inc](https://www.ribose.com). +# All rights reserved. +# This file is a part of tebako +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +require "fileutils" +require_relative "../../lib/tebako/packager/patch_helpers" + +RSpec.describe Tebako::Packager::PatchHelpers do # rubocop:disable Metrics/BlockLength + let(:temp_dir) { File.join(Dir.tmpdir, "tebako_patch_helpers_test") } + + before do + FileUtils.mkdir_p(temp_dir) + end + + after do + FileUtils.rm_rf(temp_dir) + end + + describe "#patch_file" do + let(:temp_dir) { Dir.mktmpdir } + let(:temp_file) { File.join(temp_dir, "sample.txt") } + + after do + FileUtils.remove_entry_secure(temp_dir) + end + + context "when the file does not exist" do + it "raises an error" do + expect do + Tebako::Packager::PatchHelpers.patch_file(File.join(temp_dir, "missing.txt"), { /old/ => "new" }) + end.to raise_error(Tebako::Error, /Could not patch/) + end + end + + context "when the file exists" do + before do + File.write(temp_file, "old content") + end + + it "patches the file content" do + expect do + Tebako::Packager::PatchHelpers.patch_file(temp_file, { /old/ => "new" }) + end.not_to raise_error + expect(File.read(temp_file)).to eq("new content") + end + end + end + + describe "#get_prefix_macos" do + it "returns brew prefix when brew command succeeds" do + allow(Open3).to receive(:capture2).with("brew --prefix mypackage") + .and_return(["/usr/local/Cellar/mypackage", double(exitstatus: 0)]) + + expect(Tebako::Packager::PatchHelpers.get_prefix_macos("mypackage")).to eq("/usr/local/Cellar/mypackage") + end + + it "raises an error if brew command fails" do + allow(Open3).to receive(:capture2).with("brew --prefix mypackage") + .and_return(["", double(exitstatus: 1)]) + + expect do + Tebako::Packager::PatchHelpers.get_prefix_macos("mypackage") + end.to raise_error(Tebako::Error, /brew --prefix mypackage failed/) + end + end + + describe "#get_prefix_linux" do + it "returns pkg-config libdir when pkg-config command succeeds" do + allow(Open3).to receive(:capture2).with("pkg-config --variable=libdir mypackage") + .and_return(["/usr/lib/mypackage", double(exitstatus: 0)]) + + expect(Tebako::Packager::PatchHelpers.get_prefix_linux("mypackage")).to eq("/usr/lib/mypackage") + end + + it "raises an error if pkg-config command fails" do + allow(Open3).to receive(:capture2).with("pkg-config --variable=libdir mypackage") + .and_return(["", double(exitstatus: 1)]) + + expect do + Tebako::Packager::PatchHelpers.get_prefix_linux("mypackage") + end.to raise_error(Tebako::Error, /pkg-config --variable=libdir mypackage failed/) + end + end + + describe "#exe_suffix" do + it "returns '.exe' for msys-based platforms" do + expect(Tebako::Packager::PatchHelpers.exe_suffix("msys2")).to eq(".exe") + expect(Tebako::Packager::PatchHelpers.exe_suffix("mingw32")).to eq(".exe") + expect(Tebako::Packager::PatchHelpers.exe_suffix("cygwin")).to eq(".exe") + end + + it "returns an empty string for non-msys platforms" do + expect(Tebako::Packager::PatchHelpers.exe_suffix("darwin")).to eq("") + expect(Tebako::Packager::PatchHelpers.exe_suffix("linux")).to eq("") + end + end + + describe "#restore_and_save_files" do # rubocop:disable Metrics/BlockLength + let(:ruby_source_dir) { File.join(temp_dir, "ruby_source") } + let(:test_files) { ["test1.rb", "test2.rb"] } + + before do + FileUtils.mkdir_p(ruby_source_dir) + test_files.each do |f| + File.write(File.join(ruby_source_dir, f), "original content") + File.write(File.join(ruby_source_dir, "#{f}.old"), "backup content") + end + end + + it "restores files from backups and saves existing ones" do + expect do + Tebako::Packager::PatchHelpers.restore_and_save_files(test_files, ruby_source_dir) + end.not_to raise_error + + test_files.each do |f| + target = File.join(ruby_source_dir, f) + old_file = File.join(ruby_source_dir, "#{f}.old") + expect(File.read(target)).to eq("backup content") + expect(File.read(old_file)).to eq("backup content") + end + end + + it "raises an error in strict mode if afile is missing" do + FileUtils.rm(File.join(ruby_source_dir, "test1.rb")) + expect do + Tebako::Packager::PatchHelpers.restore_and_save_files(test_files, ruby_source_dir, strict: true) + end.to raise_error(Tebako::Error) + end + + it "does not raise an error in non-strict mode if a file is missing" do + FileUtils.rm(File.join(ruby_source_dir, "test1.rb")) + expect do + Tebako::Packager::PatchHelpers.restore_and_save_files(test_files, ruby_source_dir, strict: false) + end.not_to raise_error + end + end + + describe "#yaml_reference" do + let(:ruby_version_double) { double("RubyVersion") } + + context "when Ruby 3.2" do + it "returns '-l:libyaml.a'" do + allow(ruby_version_double).to receive(:ruby32?).and_return(true) + expect(Tebako::Packager::PatchHelpers.yaml_reference(ruby_version_double)).to eq("-l:libyaml.a") + end + end + + context "otherwise" do + it "returns an empty string" do + allow(ruby_version_double).to receive(:ruby32?).and_return(false) + expect(Tebako::Packager::PatchHelpers.yaml_reference(ruby_version_double)).to eq("") + end + end + end +end diff --git a/spec/packager_spec.rb b/spec/packager_spec.rb index 0b148e42..5a87a2fb 100644 --- a/spec/packager_spec.rb +++ b/spec/packager_spec.rb @@ -214,29 +214,9 @@ it "restores and saves files for FILES_TO_RESTORE" do expect(Tebako::Packager::PatchHelpers).to receive(:restore_and_save_files) - .with(Tebako::Packager::FILES_TO_RESTORE, ruby_source_dir) + .with(Tebako::Packager::FILES_TO_RESTORE, ruby_source_dir, strict: false) described_class.pass1(ostype, ruby_source_dir, mount_point, src_dir, ruby_ver) end - - context "when ostype is linux-musl" do - let(:ostype) { "linux-musl" } - - it "restores and saves files for FILES_TO_RESTORE_MUSL" do - expect(Tebako::Packager::PatchHelpers).to receive(:restore_and_save_files) - .with(Tebako::Packager::FILES_TO_RESTORE_MUSL, ruby_source_dir) - described_class.pass1(ostype, ruby_source_dir, mount_point, src_dir, ruby_ver) - end - end - - context "when ostype is msys" do - let(:ostype) { "msys" } - - it "restores and saves files for FILES_TO_RESTORE_MSYS" do - expect(Tebako::Packager::PatchHelpers).to receive(:restore_and_save_files) - .with(Tebako::Packager::FILES_TO_RESTORE_MSYS, ruby_source_dir) - described_class.pass1(ostype, ruby_source_dir, mount_point, src_dir, ruby_ver) - end - end end describe "#pass1a" do diff --git a/tests/scripts/functional-tests.sh b/tests/scripts/functional-tests.sh index cea9daf1..a59d5e87 100755 --- a/tests/scripts/functional-tests.sh +++ b/tests/scripts/functional-tests.sh @@ -253,6 +253,7 @@ test_tebako_press_09() { # Use test-11 gemspec to build a test gem pushd "${DIR_TESTS}/test-11" > /dev/null || fail "pushd ${DIR_TESTS}/test-11 failed" + mkdir -p "${DIR_TESTS}/test-09" gem build tebako-test.gemspec -o "${DIR_TESTS}/test-09/tebako-test-0.0.2.gem" popd > /dev/null || fail "popd failed" diff --git a/tests/test-01/tebako-test-run.rb b/tests/test-01/tebako-test-run.rb index c2b5051d..d79a06b5 100755 --- a/tests/test-01/tebako-test-run.rb +++ b/tests/test-01/tebako-test-run.rb @@ -3,6 +3,7 @@ puts "Hello! This is test-01 talking from inside DwarFS" puts "Gem path: #{Gem.path}" + puts "Rubygems version: #{Gem.rubygems_version}" if defined?(TebakoRuntime::VERSION) puts "Using tebako-runtime v#{TebakoRuntime::VERSION}" diff --git a/tests/test-09/tebako-test-0.0.2.gem b/tests/test-09/tebako-test-0.0.2.gem deleted file mode 100644 index c4d25c52..00000000 Binary files a/tests/test-09/tebako-test-0.0.2.gem and /dev/null differ