Skip to content

Instantly share code, notes, and snippets.

@drbrain
Created November 10, 2016 19:48
Show Gist options
  • Save drbrain/179dfbf9655bdbc767efe5f349e8faa7 to your computer and use it in GitHub Desktop.
Save drbrain/179dfbf9655bdbc767efe5f349e8faa7 to your computer and use it in GitHub Desktop.
require 'rake/tasklib'
require 'rake/clean'
##
# Creates a cross-compiled library that mruby-cli can use to link with
# each cross-built command.
#
# CrossLibrary.new "curl" do |cross|
# cross.release_name = "curl-7.28.0"
# cross.url = "https://curl.example/download/#{cross.release}.tar.gz"
# end
#
# You must also add the cross-compiled include and library paths to your
# build_config.rb:
#
# MRuby::Build.new do |conf|
# # …
#
# conf.cc.include_paths << "cross/#{name}/include"
# conf.linker.library_paths << "cross/#{name}/lib"
#
# # …
class CrossLibrary < Rake::TaskLib
##
# Extra flags to send to `configure` when building this library.
#
# These must be specified as an Array:
#
# cross.configure_flags << '--without-ssl' << '--without-zlib'
attr_reader :configure_flags
##
# The name of the mrbgem that depends upon this library.
#
# This allows the Rakefile to cross-compile the library when it is needed.
attr_accessor :mrbgem_name
##
# The name of this release package.
#
# This must uniquely identify this release amongst other libraries of the
# same name. Usually this will include the library name and a release
# version.
#
# cross.release_name = "curl-7.28.0"
attr_accessor :release_name
##
# The URL where this release can be downloaded.
#
# The value should include #release_name in the URL.
#
# cross.release_name = "curl-7.28.0"
# cross.url = "https://curl.example/download/#{cross.release}.tar.gz"
attr_accessor :url
def initialize(library, mrbgem_name: "mruby-#{library}", &block)
@library = library
@mrbgem_name = "mruby-#{library}"
@configure_flags = []
# Used to re-run configure when the defining rake file changes
@task_source = block.source_location.first
default_flags = MRuby.targets['host'].cc.flags
targets = MRuby.targets.map do |name, build|
cflags = build.cc.flags - default_flags
env = {}
env['CC'] = build.cc.command
env['CFLAGS'] = cflags.join ' ' unless cflags.empty?
[name, env]
end
@targets = Hash[targets]
yield self
raise ArgumentError, "you must set release_name" unless @release_name
raise ArgumentError, "you must set url" unless @url
@tarball = File.basename @url
@download = "tmp/#{@tarball}"
@source_dir = "tmp/#{@release_name}"
define
end
##
# Defines compilation tasks for cross-compiling on +host+. Use +env+ to set
# environment details for the cross-compiled environment (CC, etc).
#
# The targets and environment are automatically determined from your
# build_config.rb so you should not need to edit these values.
#
# compile_tasks 'x86_64-pc-linux-gnu'
# compile_tasks 'i686-pc-linux-gnu', CFLAGS: '-m32'
# compile_tasks 'x86_64-apple-darwin14', CC: 'x86_64-apple-darwin14-clang'
# compile_tasks 'i386-apple-darwin14', CC: 'i386-apple-darwin14-clang'
def compile_tasks(host, env)
task_name = "#{@library}:#{host}"
# task environment
build_dir = File.join 'tmp', @library, 'build', host
configure = File.join @source_dir, 'configure'
configure = File.expand_path configure
install_dir = File.expand_path "cross/#{host}"
makefile = File.join build_dir, 'Makefile'
library_a = File.join install_dir, 'lib', "lib#{@library}.a"
mruby_build = MRuby.targets[host]
mrbgem = mruby_build.gems.find { |gem| gem.name == @mrbgem_name }
mrbgem_srcs = Rake::FileList["#{mrbgem.dir}/src/*.{c,cpp,cxx,cc,m,asm,s,S}"]
# define tasks
CLOBBER << install_dir
directory build_dir
CLEAN << build_dir
file makefile => [build_dir, @source_dir, @task_source, __FILE__] do
configure_command = [
configure,
'--prefix', install_dir,
]
if host != 'host' then
configure_command << '--host' << host
end
configure_command.concat configure_flags
cd build_dir do
with_env env do
sh(*configure_command)
end
end
end
file library_a => makefile do
cd build_dir do
sh 'make'
sh 'make', 'install'
end
end
mrbgem_srcs.each do |src|
file src => library_a
end
desc "Build #{@library} for #{host}"
task task_name => library_a
task "#{@library}:compile" => task_name
task_name
end
##
# Defines all necessary tasks for cross-compiling a library.
def define
directory 'tmp'
CLEAN << 'tmp'
file @download => 'tmp' do
sh 'curl', '-s', '-L',
'--fail', '--retry', '3', '--retry-delay', '1',
'-o', @download,
@url
end
CLEAN << @download
directory @source_dir => @download do
sh 'tar', 'xzf', @download, '-C', 'tmp'
end
CLEAN << @source_dir
desc 'Cross-compile curl for all platforms'
task "#{@library}:compile"
@targets.each do |target, env|
compile_tasks target, env
end
task compile: "#{@library}:compile"
end
def with_env(env)
orig_env = {}
env.each do |key, value|
key = key.to_s
orig_env[key] = ENV[key] if ENV.include? key
ENV[key] = value
end
begin
yield
ensure
env.each_key do |key|
ENV.delete key.to_s
end
orig_env.each do |key, value|
ENV[key] = value
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment