diff --git a/lib/ruby_wasm/build.rb b/lib/ruby_wasm/build.rb index 437b0550d3..3d6e8c9bf4 100644 --- a/lib/ruby_wasm/build.rb +++ b/lib/ruby_wasm/build.rb @@ -3,4 +3,34 @@ require_relative "build/toolchain" module RubyWasm + # Build executor to run the actual build commands. + class BuildExecutor + def system(*args, **kwargs) + Kernel.system(*args, **kwargs) + end + + def rm_rf(list) + FileUtils.rm_rf(list) + end + + def rm_f(list) + FileUtils.rm_f(list) + end + + def cp_r(src, dest) + FileUtils.cp_r(src, dest) + end + + def mv(src, dest) + FileUtils.mv(src, dest) + end + + def mkdir_p(list) + FileUtils.mkdir_p(list) + end + + def write(path, data) + File.write(path, data) + end + end end diff --git a/lib/ruby_wasm/build/product/baseruby.rb b/lib/ruby_wasm/build/product/baseruby.rb index 72d8fb1cc9..d2fc7baec7 100644 --- a/lib/ruby_wasm/build/product/baseruby.rb +++ b/lib/ruby_wasm/build/product/baseruby.rb @@ -20,13 +20,13 @@ def name "baseruby-#{@channel}" end - def build - FileUtils.mkdir_p product_build_dir - @source.build + def build(executor) + executor.mkdir_p product_build_dir + @source.build(executor) return if Dir.exist?(install_dir) Dir.chdir(product_build_dir) do - system "#{@source.configure_file} --prefix=#{install_dir} --disable-install-doc" - system "make install" + executor.system "#{@source.configure_file} --prefix=#{install_dir} --disable-install-doc" + executor.system "make install" end end end diff --git a/lib/ruby_wasm/build/product/crossruby.rb b/lib/ruby_wasm/build/product/crossruby.rb index 48bdf7b04a..b0511d0801 100644 --- a/lib/ruby_wasm/build/product/crossruby.rb +++ b/lib/ruby_wasm/build/product/crossruby.rb @@ -27,19 +27,22 @@ def make_args(crossruby) make_args end - def build(crossruby) + def build(executor, crossruby) lib = @name objdir = product_build_dir crossruby - FileUtils.mkdir_p objdir - do_extconf crossruby - system %Q(make -C "#{objdir}" #{make_args(crossruby).join(" ")} #{lib}.a) + executor.mkdir_p objdir + do_extconf executor, crossruby + executor.system %Q(make -C "#{objdir}" #{make_args(crossruby).join(" ")} #{lib}.a) # A ext can provide link args by link.filelist. It contains only built archive file by default. unless File.exist?(linklist(crossruby)) - File.write(linklist(crossruby), Dir.glob("#{objdir}/*.a").join("\n")) + executor.write( + linklist(crossruby), + Dir.glob("#{objdir}/*.a").join("\n") + ) end end - def do_extconf(crossruby) + def do_extconf(executor, crossruby) objdir = product_build_dir crossruby source = crossruby.source extconf_args = [ @@ -63,14 +66,14 @@ def do_extconf(crossruby) "-I#{crossruby.build_dir}" ] # Clear RUBYOPT to avoid loading unrelated bundle setup - system ({ "RUBYOPT" => "" }), - "#{crossruby.baseruby_path} #{extconf_args.join(" ")}", - chdir: objdir + executor.system ({ "RUBYOPT" => "" }), + "#{crossruby.baseruby_path} #{extconf_args.join(" ")}", + chdir: objdir end - def do_install_rb(crossruby) + def do_install_rb(executor, crossruby) objdir = product_build_dir crossruby - system %Q(make -C "#{objdir}" #{make_args(crossruby).join(" ")} install-rb) + executor.system %Q(make -C "#{objdir}" #{make_args(crossruby).join(" ")} install-rb) end def cache_key(digest) @@ -123,45 +126,48 @@ def initialize( super(@params.target, @toolchain) end - def configure(reconfigure: false) + def configure(executor, reconfigure: false) if !File.exist?("#{build_dir}/Makefile") || reconfigure args = configure_args(RbConfig::CONFIG["host"], toolchain) - system "#{source.configure_file} #{args.join(" ")}", chdir: build_dir + executor.system "#{source.configure_file} #{args.join(" ")}", + chdir: build_dir end # NOTE: we need rbconfig.rb at configuration time to build user given extensions with mkmf - system "make rbconfig.rb", chdir: build_dir + executor.system "make rbconfig.rb", chdir: build_dir end - def build_exts - @user_exts.each { |prod| prod.build(self) } - FileUtils.mkdir_p File.dirname(extinit_obj) - system %Q(ruby #{extinit_c_erb} #{@user_exts.map(&:name).join(" ")} | #{toolchain.cc} -c -x c - -o #{extinit_obj}) + def build_exts(executor) + @user_exts.each { |prod| prod.build(executor, self) } + executor.mkdir_p File.dirname(extinit_obj) + executor.system %Q(ruby #{extinit_c_erb} #{@user_exts.map(&:name).join(" ")} | #{toolchain.cc} -c -x c - -o #{extinit_obj}) end - def build(remake: false, reconfigure: false) - FileUtils.mkdir_p dest_dir - FileUtils.mkdir_p build_dir + def build(executor, remake: false, reconfigure: false) + executor.mkdir_p dest_dir + executor.mkdir_p build_dir @toolchain.install - [@source, @baseruby, @libyaml, @zlib, @openssl, @wasi_vfs].each(&:build) - configure(reconfigure: reconfigure) - build_exts + [@source, @baseruby, @libyaml, @zlib, @openssl, @wasi_vfs].each do |prod| + prod.build(executor) + end + configure(executor, reconfigure: reconfigure) + build_exts(executor) install_dir = File.join(build_dir, "install") if !File.exist?(install_dir) || remake || reconfigure - system "make install DESTDIR=#{install_dir}", chdir: build_dir + executor.system "make install DESTDIR=#{install_dir}", chdir: build_dir end - FileUtils.rm_rf dest_dir - FileUtils.cp_r install_dir, dest_dir - @user_exts.each { |ext| ext.do_install_rb(self) } - system "tar cfz #{artifact} -C rubies #{name}" + executor.rm_rf dest_dir + executor.cp_r install_dir, dest_dir + @user_exts.each { |ext| ext.do_install_rb(executor, self) } + executor.system "tar cfz #{artifact} -C rubies #{name}" end - def clean - FileUtils.rm_rf dest_dir - FileUtils.rm_rf build_dir - FileUtils.rm_rf ext_build_dir - FileUtils.rm_f artifact + def clean(executor) + executor.rm_rf dest_dir + executor.rm_rf build_dir + executor.rm_rf ext_build_dir + executor.rm_f artifact end def name diff --git a/lib/ruby_wasm/build/product/libyaml.rb b/lib/ruby_wasm/build/product/libyaml.rb index efe781ed0b..6d8c588d37 100644 --- a/lib/ruby_wasm/build/product/libyaml.rb +++ b/lib/ruby_wasm/build/product/libyaml.rb @@ -28,20 +28,22 @@ def name product_build_dir end - def build + def build(executor) return if Dir.exist?(install_root) - FileUtils.mkdir_p File.dirname(product_build_dir) - FileUtils.rm_rf product_build_dir - system "curl -L https://github.com/yaml/libyaml/releases/download/#{LIBYAML_VERSION}/yaml-#{LIBYAML_VERSION}.tar.gz | tar xz", - chdir: File.dirname(product_build_dir) + executor.mkdir_p File.dirname(product_build_dir) + executor.rm_rf product_build_dir + executor.system "curl -L https://github.com/yaml/libyaml/releases/download/#{LIBYAML_VERSION}/yaml-#{LIBYAML_VERSION}.tar.gz | tar xz", + chdir: File.dirname(product_build_dir) # obtain the latest config.guess and config.sub for Emscripten and WASI triple support - system "curl -o #{product_build_dir}/config/config.guess 'https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD'" - system "curl -o #{product_build_dir}/config/config.sub 'https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD'" + executor.system "curl -o #{product_build_dir}/config/config.guess 'https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD'" + executor.system "curl -o #{product_build_dir}/config/config.sub 'https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD'" - system "./configure #{configure_args.join(" ")}", chdir: product_build_dir - system "make install DESTDIR=#{destdir}", chdir: product_build_dir + executor.system "./configure #{configure_args.join(" ")}", + chdir: product_build_dir + executor.system "make install DESTDIR=#{destdir}", + chdir: product_build_dir end end end diff --git a/lib/ruby_wasm/build/product/openssl.rb b/lib/ruby_wasm/build/product/openssl.rb index 160ad7c907..f653293e54 100644 --- a/lib/ruby_wasm/build/product/openssl.rb +++ b/lib/ruby_wasm/build/product/openssl.rb @@ -54,18 +54,20 @@ def configure_args args + tools_args end - def build + def build(executor) return if Dir.exist?(install_root) - FileUtils.mkdir_p File.dirname(product_build_dir) - FileUtils.rm_rf product_build_dir - system "curl -L https://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz | tar xz", - chdir: File.dirname(product_build_dir) + executor.mkdir_p File.dirname(product_build_dir) + executor.rm_rf product_build_dir + executor.system "curl -L https://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz | tar xz", + chdir: File.dirname(product_build_dir) - system "./Configure #{configure_args.join(" ")}", chdir: product_build_dir + executor.system "./Configure #{configure_args.join(" ")}", + chdir: product_build_dir # Use "install_sw" instead of "install" because it tries to install docs and it's very slow. # OpenSSL build system doesn't have well support for parallel build, so force -j1. - system "make -j1 install_sw DESTDIR=#{destdir}", chdir: product_build_dir + executor.system "make -j1 install_sw DESTDIR=#{destdir}", + chdir: product_build_dir end end end diff --git a/lib/ruby_wasm/build/product/ruby_source.rb b/lib/ruby_wasm/build/product/ruby_source.rb index 4c59db7953..4d582c774b 100644 --- a/lib/ruby_wasm/build/product/ruby_source.rb +++ b/lib/ruby_wasm/build/product/ruby_source.rb @@ -31,37 +31,39 @@ def configure_file File.join(src_dir, "configure") end - def fetch + def fetch(executor) case @params[:type] when "github" repo_url = "https://github.com/#{@params[:repo]}.git" - FileUtils.mkdir_p src_dir - system "git init", chdir: src_dir - system "git remote add origin #{repo_url}", chdir: src_dir - system( + executor.mkdir_p src_dir + executor.system "git init", chdir: src_dir + executor.system "git remote add origin #{repo_url}", chdir: src_dir + executor.system( "git fetch --depth 1 origin #{@params[:rev]}:origin/#{@params[:rev]}", chdir: src_dir ) or raise "failed to clone #{repo_url}" - system("git checkout origin/#{@params[:rev]}", chdir: src_dir) or - raise "failed to checkout #{@params[:rev]}" + executor.system( + "git checkout origin/#{@params[:rev]}", + chdir: src_dir + ) or raise "failed to checkout #{@params[:rev]}" when "local" - FileUtils.mkdir_p File.dirname(src_dir) - FileUtils.cp_r @params[:src], src_dir + executor.mkdir_p File.dirname(src_dir) + executor.cp_r @params[:src], src_dir else raise "unknown source type: #{@params[:type]}" end (@params[:patches] || []).each do |patch_path| - system "patch -p1 < #{patch_path}", chdir: src_dir + executor.system "patch -p1 < #{patch_path}", chdir: src_dir end end - def build - fetch unless File.exist?(src_dir) + def build(executor) + fetch(executor) unless File.exist?(src_dir) unless File.exist?(configure_file) Dir.chdir(src_dir) do - system "ruby tool/downloader.rb -d tool -e gnu config.guess config.sub" or + executor.system "ruby tool/downloader.rb -d tool -e gnu config.guess config.sub" or raise "failed to download config.guess and config.sub" - system "./autogen.sh" or raise "failed to run autogen.sh" + executor.system "./autogen.sh" or raise "failed to run autogen.sh" end end end diff --git a/lib/ruby_wasm/build/product/wasi_vfs.rb b/lib/ruby_wasm/build/product/wasi_vfs.rb index 62eacde4e6..3ca22a4ed0 100644 --- a/lib/ruby_wasm/build/product/wasi_vfs.rb +++ b/lib/ruby_wasm/build/product/wasi_vfs.rb @@ -40,16 +40,16 @@ def name lib_product_build_dir end - def build + def build(executor) return if !@need_fetch_lib || File.exist?(lib_wasi_vfs_a) require "tmpdir" lib_wasi_vfs_url = "https://github.com/kateinoigakukun/wasi-vfs/releases/download/v#{WASI_VFS_VERSION}/libwasi_vfs-wasm32-unknown-unknown.zip" Dir.mktmpdir do |tmpdir| - system "curl -L #{lib_wasi_vfs_url} -o #{tmpdir}/libwasi_vfs.zip" - system "unzip #{tmpdir}/libwasi_vfs.zip -d #{tmpdir}" - FileUtils.mkdir_p File.dirname(lib_wasi_vfs_a) - FileUtils.mv File.join(tmpdir, "libwasi_vfs.a"), lib_wasi_vfs_a + executor.system "curl -L #{lib_wasi_vfs_url} -o #{tmpdir}/libwasi_vfs.zip" + executor.system "unzip #{tmpdir}/libwasi_vfs.zip -d #{tmpdir}" + executor.mkdir_p File.dirname(lib_wasi_vfs_a) + executor.mv File.join(tmpdir, "libwasi_vfs.a"), lib_wasi_vfs_a end end diff --git a/lib/ruby_wasm/build/product/zlib.rb b/lib/ruby_wasm/build/product/zlib.rb index 427cb03415..9b27b5ef13 100644 --- a/lib/ruby_wasm/build/product/zlib.rb +++ b/lib/ruby_wasm/build/product/zlib.rb @@ -34,19 +34,20 @@ def configure_args args + tools_args end - def build + def build(executor) return if Dir.exist?(install_root) - FileUtils.mkdir_p File.dirname(product_build_dir) - FileUtils.rm_rf product_build_dir + executor.mkdir_p File.dirname(product_build_dir) + executor.rm_rf product_build_dir - system "curl -L https://zlib.net/zlib-#{ZLIB_VERSION}.tar.gz | tar xz", - chdir: File.dirname(product_build_dir), - exception: true + executor.system "curl -L https://zlib.net/zlib-#{ZLIB_VERSION}.tar.gz | tar xz", + chdir: File.dirname(product_build_dir), + exception: true - system "#{configure_args.join(" ")} ./configure --static", - chdir: product_build_dir - system "make install DESTDIR=#{destdir}", chdir: product_build_dir + executor.system "#{configure_args.join(" ")} ./configure --static", + chdir: product_build_dir + executor.system "make install DESTDIR=#{destdir}", + chdir: product_build_dir end end end diff --git a/lib/ruby_wasm/rake_task.rb b/lib/ruby_wasm/rake_task.rb index e08f8fc9bb..62c2b06793 100644 --- a/lib/ruby_wasm/rake_task.rb +++ b/lib/ruby_wasm/rake_task.rb @@ -71,20 +71,22 @@ def initialize( @crossruby.with_wasi_vfs @wasi_vfs @crossruby.with_openssl @openssl + executor = RubyWasm::BuildExecutor.new + desc "Cross-build Ruby for #{@target}" task name do next if File.exist? @crossruby.artifact - @crossruby.build + @crossruby.build(executor) end namespace name do task :remake do - @crossruby.build(remake: true) + @crossruby.build(executor, remake: true) end task :reconfigure do - @crossruby.build(reconfigure: true) + @crossruby.build(executor, reconfigure: true) end task :clean do - @crossruby.clean + @crossruby.clean(executor) end end end