rb_sys-0.9.87/0000755000004100000410000000000014557210256013157 5ustar www-datawww-datarb_sys-0.9.87/LICENSE-MIT0000644000004100000410000000207614557210255014617 0ustar www-datawww-dataThe MIT License (MIT) Copyright (c) 2021-2022 Ian Ker-Seymer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rb_sys-0.9.87/certs/0000755000004100000410000000000014557210255014276 5ustar www-datawww-datarb_sys-0.9.87/certs/ianks.pem0000644000004100000410000000236514557210255016114 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIDfDCCAmSgAwIBAgIBATANBgkqhkiG9w0BAQsFADBCMRQwEgYDVQQDDAtpLmtl cnNleW1lcjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYD Y29tMB4XDTIzMDQxNDEzMzYxNVoXDTI0MDQxMzEzMzYxNVowQjEUMBIGA1UEAwwL aS5rZXJzZXltZXIxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixk ARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMJ2pG+er4cP PasxsMIKL9/tmLL4gh80EMuF3SCS0qZoh+Oo8dkvRYxW8NXdwEIcp3cCNgE+5G+J TCMOVF8S15n1Z1P7xxXiXxa/BIofKKbtatVRngm14uR/6pjdkvLXqlrWdS57bNwv 7LtpzYVfDHfsl/qRWaEi4jq00PNCRSWjcva8teqswjBg8KlwGtyygpezPbVSWP8Y vzWZmVF7fqRBXU78Ah0+pNOhslBXDTvI3xJdN4hQ3H7rLjpD/qxKWq/8o+Qvx6cX dNZ3ugH/Pr3BAsqt4JFLXin9AK7PO9GDMH5JXJrUb+hAt2VNIZqpz9VlKA6BA0jN eWGea+yCZkECAwEAAaN9MHswCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O BBYEFOkrF6hsocaIMOjR/K3JBzyXCLJPMCAGA1UdEQQZMBeBFWkua2Vyc2V5bWVy QGdtYWlsLmNvbTAgBgNVHRIEGTAXgRVpLmtlcnNleW1lckBnbWFpbC5jb20wDQYJ KoZIhvcNAQELBQADggEBAMAohCl0cdVlYrJl9viGtzbEyLV/Krn0ZD/LM9d2cIRj WBu7HRkP6AfYVzhZwdgUgDYw67d715kOMpLNGeWQw3QGEHFqbVzRML64jL8IxuTz J8ttZVqM8f4GrHyISJnL92u6iP4WzdbVqx89EFjrrHd14OJXK+ZpdRh0YvnN/vMJ t2EcDweRHkN/MEdrwbH+PbYjELnKcTIEZqcCWTk8pgssGrNyjkMhxGSsSUo9qTz0 DIZ6NVmpBvohJVCCCDxQQxFKLXZp1ivoxjN+m7eJSW7yzIz062pH4u8pPNQsiVSb I5rgRPbDr2rAFGXKoQ0+u6CLkRxqrVsITl/OPfZhBQI= -----END CERTIFICATE----- rb_sys-0.9.87/exe/0000755000004100000410000000000014557210255013737 5ustar www-datawww-datarb_sys-0.9.87/exe/rb-sys-dock0000755000004100000410000002776314557210255016041 0ustar www-datawww-data#!/usr/bin/env ruby $LOAD_PATH.unshift File.expand_path("../lib", __dir__) require "optparse" require "open3" require "rb_sys/version" require "rb_sys/toolchain_info" require "rb_sys/cargo/metadata" require "rb_sys/util/logger" require "fileutils" require "tmpdir" OPTIONS = { docker_platform: "linux/amd64", version: RbSys::VERSION, directory: Dir.pwd } def cargo_metadata @cargo_metadata ||= RbSys::Cargo::Metadata.new("rb-sys-dock", deps: true) end def logger return @logger if @logger io = ARGV.include?("--quiet") ? File.open(File::NULL, "w") : $stderr @logger ||= RbSys::Util::Logger.new(io: io) end def list_platforms RbSys::ToolchainInfo.supported.each do |p| old = logger.io logger.io = $stdout puts "- #{p.platform}" ensure logger.io = old end end OptionParser.new do |opts| opts.banner = <<~MSG Usage: rb-sys-dock [OPTIONS] [COMMAND] A CLI to facillitate building Ruby on Rust extensions using Docker. Examples: Build for Linux (x86_64) $ rb-sys-dock --platform x86_64-linux --build Build for macOS (Ruby 3.1 and 3.2) $ rb-sys-dock -p arm64-darwin --ruby-versions 3.1,3.2 --build Enter a shell $ rb-sys-dock --list-platforms Run a command in the container with a specific directory mounted $ rb-sys-dock -p x64-mingw-ucrt -d /tmp/mygem -- "env | grep RUBY" List all supported platforms $ rb-sys-dock --list-platforms Options: MSG opts.on("--quiet", "Prints no logging output") do end opts.on("-v", "--version", "Prints version") do require "rb_sys/version" puts RbSys::VERSION exit end opts.on("--build", "Run the default command to cross-compile a gem") do OPTIONS[:build] = true end opts.on("-p", "--platform PLATFORM", "Platform to build for (i.e. x86_64-linux)") do |p| toolchain_info = begin RbSys::ToolchainInfo.new(p) rescue supported_list = RbSys::ToolchainInfo.all supported_list.select!(&:supported?) list = supported_list.map { |p| "- #{p} (#{p.rust_target})" }.join("\n") logger.error("Platform #{p} is not supported, please use one of:\n\n#{list}") exit(1) end OPTIONS[:platform] = p OPTIONS[:toolchain_info] = toolchain_info end opts.on("-r", "--ruby-versions LIST", "List all supported Ruby versions") do |arg| vers = arg.split(/[^0-9.]+/).map do |v| parts = v.split(".") parts[2] = "0" if parts[2].nil? parts.join(".") end OPTIONS[:ruby_versions] = vers logger.info("Building for Ruby requested versions: #{vers}") ENV["RUBY_CC_VERSION"] = vers.join(":") end opts.on("--tag TAG", "Use a specific version of the Docker image") do |tag| logger.info("Using version #{tag} of the Docker image") OPTIONS[:version] = tag OPTIONS[:no_cache] = tag == "latest" end opts.on("--list-platforms", "--list", "List all supported platforms") do logger.info("Supported platforms listed below:") list_platforms exit(0) end opts.on("-h", "--help", "Prints this help") do puts opts exit end opts.on("-V", "--verbose", "Prints verbose output") do ENV["LOG_LEVEL"] = "trace" ENV["VERBOSE"] = "1" logger.level = :trace OPTIONS[:verbose] = true end opts.on("--mount-toolchains", "Mount local Rustup toolchain (instead of pre-installed from Docker container)") do OPTIONS[:mount_rustup_toolchains] = true end opts.on("-d", "--directory DIR", "Directory to run the command in") do |val| OPTIONS[:directory] = File.expand_path(val) end end.parse! def default_docker_command return @default_docker_command if defined?(@default_docker_command) @default_docker_command = ENV.fetch("DOCKER") do if !(docker = `which docker`).empty? docker.strip elsif !(podman = `which podman`).empty? podman.strip else logger.fatal("Could not find docker or podman command, please install one of them") end end end def run_command!(*cmd) logger.trace("Running command:\n\t$ #{cmd.join(" ")}") stdout, stderr, status = Open3.capture3(*cmd) if status.success? stdout else logger.error("Error running command: $ #{cmd}") warn(stderr) exit(status.exitstatus) end end def docker(cmd) run_command!("#{default_docker_command} #{cmd}") end def determine_cache_dir return ENV["RB_SYS_DOCK_CACHE_DIR"] if ENV["RB_SYS_DOCK_CACHE_DIR"] return File.join(ENV["XDG_CACHE_HOME"], "rb-sys-dock") if ENV["XDG_CACHE_HOME"] File.join(ENV["HOME"], ".cache", "rb-sys-dock") end def docker_tmp @docker_tmp ||= "/tmp/rb-sys-dock" end def docker_bundle_home @docker_bundle_home ||= "/tmp/rb-sys-dock/bundle-home" end def cache_dir return @cache_dir if defined?(@cache_dir) @cache_dir = determine_cache_dir FileUtils.mkdir_p(@cache_dir) @cache_dir end def mount_cargo_registry local_registry_dir = if ENV["CARGO_HOME"] ENV["CARGO_HOME"] elsif File.exist?(cargo_home = File.join(ENV["HOME"], ".cargo")) cargo_home else File.join(cache_dir, "cargo") end dir = File.join("registry") logger.trace("Mounting cargo registry dir: #{dir}") FileUtils.mkdir_p(dir) mount_shared_bind_dir(File.join(local_registry_dir, dir), File.join("/usr/local/cargo", dir)) end def mount_rustup_toolchains return unless OPTIONS[:mount_rustup_toolchains] local_rustup_dir = if OPTIONS[:mount_rustup_toolchains].is_a?(String) OPTIONS[:mount_rustup_toolchains] elsif ENV["RUSTUP_HOME"] ENV["RUSTUP_HOME"] elsif File.exist?(rustup_home = File.join(ENV["HOME"], ".rustup")) rustup_home else logger.fatal("Could not find Rustup home directory, please set RUSTUP_HOME") end logger.info("Mounting rustup toolchains from #{local_rustup_dir}") target_triple = OPTIONS[:toolchain_info].rust_target dkr_triple = "x86_64-unknown-linux-gnu" dkr_toolchain = "stable-#{dkr_triple}" dkr_toolchain_dir = "/usr/local/rustup/toolchains/#{dkr_toolchain}" installed_toolchains = Dir.glob(File.join(local_rustup_dir, "toolchains", "*")).map { |f| File.basename(f) } has_host_toolchain = installed_toolchains.any? { |t| t.end_with?(dkr_toolchain) } if !has_host_toolchain logger.info("Installing default toolchain for docker image (#{dkr_toolchain})") run_command!("rustup", "toolchain", "add", dkr_toolchain, "--force-non-host") end has_target = run_command!("rustup target list --installed --toolchain #{dkr_toolchain}").include?(target_triple) if !has_target logger.info("Installing target for docker image (#{target_triple})") run_command!("rustup", "target", "add", target_triple, "--toolchain", dkr_toolchain) end volume("#{local_rustup_dir}/toolchains/#{dkr_toolchain}", dkr_toolchain_dir, mode: "z,ro") end def volume(src, dest, mode: "rw") "--volume #{src}:#{dest}:rw" end def mount_bundle_cache dir = File.join(cache_dir, ruby_platform, "bundle") bundle_path = File.join(docker_tmp, "bundle") FileUtils.mkdir_p(dir) logger.trace("Mounting bundle cache: #{dir}") "#{volume(dir, bundle_path)} -e BUNDLE_PATH=#{bundle_path.inspect}" end def mount_bundle_config bundle_config = File.join(ENV["HOME"] || "~", ".bundle", "config") return unless File.exist?(bundle_config) logger.trace("Mounting bundle config: #{bundle_config}") parts = [] parts << volume(bundle_config, File.join(docker_bundle_home, ".bundle", "config"), mode: "ro") parts << "-e BUNDLE_HOME=#{docker_bundle_home.inspect}" parts.join(" ") end def tmp_target_dir return @tmp_target_dir if defined?(@tmp_target_dir) dir = File.join(working_directory, "tmp", "rb-sys-dock", ruby_platform, "target") FileUtils.mkdir_p(dir) @tmp_target_dir = dir end def working_directory OPTIONS.fetch(:directory) end def mount_target_dir "-v #{tmp_target_dir}:#{File.join(working_directory, "target")}" end def mount_command_history return unless $stdin.tty? history_dir = File.join(cache_dir, OPTIONS.fetch(:platform), "commandhistory") FileUtils.mkdir_p(history_dir) "-v #{history_dir}:#{File.join(docker_tmp, "commandhistory")}" end def default_command_to_run(input_args) input_cmd = input_args.empty? ? "true" : input_args.join(" ") if OPTIONS[:build] with_bundle = +"test -f Gemfile && bundle install && #{input_cmd} && bundle exec rake native:$RUBY_TARGET gem" without_bundle = "#{input_cmd} && rake native:$RUBY_TARGET gem" logger.info("Running default build command (rake native:#{ruby_platform} gem)") "bash -c '(#{with_bundle}) || (#{without_bundle})'" else input_args.empty? ? "bash" : "bash -c '#{input_args.join(" ")}'" end end def uid_gid explicit_uid = ENV["RB_SYS_DOCK_UID"] explicit_gid = ENV["RB_SYS_DOCK_GID"] if /darwin/.match?(RbConfig::CONFIG["host_os"]) && !default_docker_command.include?("podman") [explicit_uid || "1000", explicit_gid || "1000"] else [explicit_uid || Process.uid, explicit_gid || Process.gid] end end def user_mapping uid, gid = uid_gid "-e UID=#{uid} -e GID=#{gid} -e GROUP=_staff -e USER=rb-sys-dock" end def interactive?(input_args) $stdin.tty? end def mount_shared_bind_dir(src, dest) "--mount type=bind,source=#{src},destination=#{dest},readonly=false" end def mount_tmp_dir "--mount type=bind,source=#{Dir.mktmpdir},destination=#{working_directory}/tmp/#{ruby_platform},readonly=false" end def toolchain_info @toolchain_info ||= OPTIONS.fetch(:toolchain_info) do logger.error("Could not determine ruby platform, please set ruby platform with --platform to one of:") list_platforms logger.fatal("Exiting...") end end def ruby_platform @ruby_platform ||= toolchain_info.platform end def rcd(input_args) wrapper_command = [] wrapper_command << "sigfw" unless interactive?(input_args) wrapper_command << "runas" docker_options = [] docker_options << "--tty" if interactive?(input_args) cmd = <<~SH #{default_docker_command} run \ --platform #{OPTIONS.fetch(:docker_platform)} \ -v #{working_directory}:#{working_directory} \ #{mount_tmp_dir} \ #{mount_target_dir} \ #{mount_cargo_registry} \ #{mount_rustup_toolchains} \ #{mount_bundle_cache} \ #{mount_bundle_config} \ #{mount_command_history} \ #{user_mapping} \ -e GEM_PRIVATE_KEY_PASSPHRASE \ -e ftp_proxy \ -e http_proxy \ -e https_proxy \ -e RCD_HOST_RUBY_PLATFORM=#{RbConfig::CONFIG["arch"]} \ -e RCD_HOST_RUBY_VERSION=#{RUBY_VERSION} \ -e RCD_IMAGE \ -e RB_SYS_DOCK_TMPDIR="/tmp/rb-sys-dock" \ -e RB_SYS_CARGO_TARGET_DIR=#{tmp_target_dir.inspect} \ #{ENV["RUBY_CC_VERSION"] ? "-e RUBY_CC_VERSION=#{ENV["RUBY_CC_VERSION"]}" : ""} \ -e RAKEOPT \ -e TERM \ -w #{working_directory} \ --rm \ --interactive \ #{docker_options.join(" ")} \ #{ENV.fetch("RCD_IMAGE")} \ #{wrapper_command.join(" ")} \ #{default_command_to_run(input_args)} SH cmd.gsub!(/\s+/, " ") logger.trace("Running command:\n\t$ #{cmd}") exec(cmd) end def download_image image = ENV.fetch("RCD_IMAGE") if docker("images -q #{image}").strip.empty? || OPTIONS[:no_cache] # Nicely formatted message that we are downloading the image which might take awhile logger.info("Downloading container #{image.inspect}, this might take awhile...") docker("pull #{image} --platform #{OPTIONS[:docker_platform]} --quiet > /dev/null") end end def log_some_useful_info return if OPTIONS[:build] if ARGV.empty? logger.info("Entering shell in Docker container #{ENV["RCD_IMAGE"].inspect}") else logger.info("Running command #{ARGV.inspect} in Docker container #{ENV["RCD_IMAGE"].inspect}") end end def set_env ENV["RCD_IMAGE"] ||= "rbsys/#{ruby_platform}:#{OPTIONS[:version]}" end def lint_rb_sys cargo_version = cargo_metadata.rb_sys_version return if cargo_version == RbSys::VERSION logger.warn("Cargo rb-sys version (#{cargo_version}) does not match Ruby gem version (#{RbSys::VERSION})") rescue => e logger.warn("Could not determine Cargo rb-sys version") logger.trace("Error was: #{e.inspect}") end set_env lint_rb_sys download_image log_some_useful_info rcd(ARGV) rb_sys-0.9.87/lib/0000755000004100000410000000000014557210255013724 5ustar www-datawww-datarb_sys-0.9.87/lib/rb_sys.rb0000644000004100000410000000023414557210255015551 0ustar www-datawww-data# frozen_string_literal: true require_relative "rb_sys/version" require_relative "rb_sys/error" require_relative "rb_sys/toolchain_info" module RbSys end rb_sys-0.9.87/lib/rb_sys/0000755000004100000410000000000014557210255015225 5ustar www-datawww-datarb_sys-0.9.87/lib/rb_sys/cargo_builder/0000755000004100000410000000000014557210255020026 5ustar www-datawww-datarb_sys-0.9.87/lib/rb_sys/cargo_builder/link_flag_converter.rb0000644000004100000410000000170514557210255024373 0ustar www-datawww-data# frozen_string_literal: true require "shellwords" module RbSys class CargoBuilder < Gem::Ext::Builder # Converts Ruby link flags into something cargo understands # @api private class LinkFlagConverter FILTERED_PATTERNS = [ /compress-debug-sections/, # Not supported by all linkers, and not required for Rust /^\s*-s\s*$/ ] def self.convert(args) Shellwords.split(args).flat_map { |arg| convert_arg(arg) } end def self.convert_arg(arg) return [] if FILTERED_PATTERNS.any? { |p| p.match?(arg) } case arg.chomp when /^-L\s*(.+)$/ ["-L", "native=#{$1}"] when /^--library=(\w+\S+)$/, /^-l\s*(\w+\S+)$/ ["-l", $1] when /^-l\s*:lib(\S+).(so|dylib|dll)$/ ["-l", "dylib=#{$1}"] when /^-F\s*(.*)$/ ["-l", "framework=#{$1}"] else ["-C", "link-arg=#{arg}"] end end end end end rb_sys-0.9.87/lib/rb_sys/cargo/0000755000004100000410000000000014557210255016320 5ustar www-datawww-datarb_sys-0.9.87/lib/rb_sys/cargo/metadata.rb0000644000004100000410000000630414557210255020430 0ustar www-datawww-data# frozen_string_literal: true require "open3" require "psych" module RbSys module Cargo # Extracts metadata from a Cargo project using `cargo metadata`. class Metadata attr_reader :name # Initializes a new Cargo::Metadata instance. # # @param name [String] the name of the Cargo project def initialize(name, deps: false) raise ArgumentError, "name must be a String" unless name.is_a?(String) @name = name @cargo_metadata = nil @package_metadata = nil @deps = deps end # Returns the path where the Cargo project's Cargo.toml is located. # # @return [String] def manifest_directory @manifest_directory ||= File.dirname(manifest_path) end # Returns the target directory for the Cargo project. # # @return [String] def target_directory cargo_metadata.fetch("target_directory") end # Returns the workspace root for the Cargo project. # # @return [String] def workspace_root cargo_metadata.fetch("workspace_root") end # Returns the workspace members for the Cargo project. # # @return [Array] def packages cargo_metadata.fetch("packages") end # Returns the path to the package's Cargo.toml. # # @return [String] def manifest_path package_metadata.fetch("manifest_path") end # Returns the package's version. # # @return [String] def version package_metadata.fetch("version") end # Returns the package's id. # # @return [String] def id package_metadata.fetch("id") end # Returns the package's Rust edition. # # @return [String] def edition package_metadata.fetch("edition") end # Returns the package's features. # # @return [Array] def features package_metadata.fetch("features") end # Returns the package's custom metadata. # # @return [Hash] def metadata package_metadata.fetch("metadata") end # Returns the rb-sys version, if any. def rb_sys_version pkg = packages.find { |p| p.fetch("name") == "rb-sys" } return unless pkg pkg["version"] end private def package_metadata return @package_metadata if @package_metadata found = cargo_metadata.fetch("packages").find { |p| p.fetch("name") == name } raise PackageNotFoundError, @name unless found @package_metadata = found end def cargo_metadata return @cargo_metadata if @cargo_metadata ::Gem.load_yaml cargo = ENV["CARGO"] || "cargo" args = ["metadata", "--format-version", "1"] args << "--no-deps" unless @deps out, stderr, status = Open3.capture3(cargo, *args) raise "exited with non-zero status (#{status})" unless status.success? data = Gem::SafeYAML.safe_load(out) raise "metadata must be a Hash" unless data.is_a?(Hash) @cargo_metadata = data rescue => err raise CargoMetadataError.new(err, stderr) end end end end rb_sys-0.9.87/lib/rb_sys/error.rb0000644000004100000410000000235314557210255016706 0ustar www-datawww-data# frozen_string_literal: true module RbSys # Error is the base class for all errors raised by rb_sys. class Error < StandardError; end # Raised when a package is not found from the Cargo metadata. class PackageNotFoundError < Error def initialize(name) msg = <<~MSG.chomp.tr("\n", " ") Could not find Cargo package metadata for #{@name.inspect}. Please check that #{@name.inspect} matches the crate name in your Cargo.toml." MSG super(msg) end end # Raised when Cargo metadata cannot be parsed. class CargoMetadataError < Error def initialize(err, stderr) msg = <<~MSG.chomp.tr("\n", " ") Could not infer Rust crate information using `cargo metadata`. Original error was: #{err.class}: #{err.message} Things to check: - Check that your ext/*/Cargo.toml at is valid - If you are using a workspace, make sure you are the root Cargo.toml exists - Make sure `cargo` is installed and in your PATH MSG if !stderr.empty? indented_stderr = stderr.lines.map { |line| " #{line}" }.join msg << "Stderr from `cargo metadata` was:\n#{indented_stderr}" end super(msg) end end end rb_sys-0.9.87/lib/rb_sys/cargo.rb0000644000004100000410000000017614557210255016651 0ustar www-datawww-data# frozen_string_literal: true module RbSys # Helpers for Cargo module Cargo end end require_relative "cargo/metadata" rb_sys-0.9.87/lib/rb_sys/toolchain_info/0000755000004100000410000000000014557210255020220 5ustar www-datawww-datarb_sys-0.9.87/lib/rb_sys/toolchain_info/data.rb0000644000004100000410000000367314557210255021467 0ustar www-datawww-data# frozen_string_literal: true # THIS FILE IS AUTO-GENERATED BY `rake data:derive` module RbSys class ToolchainInfo # @private DATA = {"arm-linux" => {"rust-target" => "arm-unknown-linux-gnueabihf", "rake-compiler-dock" => {"cc" => "arm-linux-gnueabihf-gcc"}, "docker-platform" => "linux/arm/v7", "supported" => true}, "aarch64-linux" => {"rust-target" => "aarch64-unknown-linux-gnu", "rake-compiler-dock" => {"cc" => "aarch64-linux-gnu-gcc"}, "docker-platform" => "linux/arm64/v8", "supported" => true}, "arm64-darwin" => {"rust-target" => "aarch64-apple-darwin", "rake-compiler-dock" => {"cc" => "aarch64-apple-darwin-clang"}, "docker-platform" => "linux/arm64/v8", "supported" => true}, "x64-mingw-ucrt" => {"rust-target" => "x86_64-pc-windows-gnu", "rake-compiler-dock" => {"cc" => "x86_64-windows-gnu-gcc"}, "docker-platform" => "linux/amd64", "supported" => true}, "x64-mingw32" => {"rust-target" => "x86_64-pc-windows-gnu", "rake-compiler-dock" => {"cc" => "x86_64-windows-gnu-gcc"}, "docker-platform" => "linux/amd64", "supported" => true}, "x86-linux" => {"rust-target" => "i686-unknown-linux-gnu", "rake-compiler-dock" => {"cc" => "i686-redhat-linux-gcc"}, "docker-platform" => "linux/i386", "supported" => false}, "x86-mingw32" => {"rust-target" => "i686-pc-windows-gnu", "rake-compiler-dock" => {"cc" => "i686-w64-mingw32-gcc"}, "docker-platform" => "linux/i386", "supported" => false}, "x86_64-darwin" => {"rust-target" => "x86_64-apple-darwin", "rake-compiler-dock" => {"cc" => "x86_64-apple-darwin-clang"}, "docker-platform" => "linux/amd64", "supported" => true}, "x86_64-linux" => {"rust-target" => "x86_64-unknown-linux-gnu", "rake-compiler-dock" => {"cc" => "x86_64-redhat-linux-gcc"}, "docker-platform" => "linux/amd64", "supported" => true}, "x86_64-linux-musl" => {"rust-target" => "x86_64-unknown-linux-musl", "rake-compiler-dock" => {"cc" => "x86_64-unknown-linux-musl-gcc"}, "docker-platform" => "linux/amd64", "supported" => true}} end end rb_sys-0.9.87/lib/rb_sys/mkmf.rb0000644000004100000410000003060714557210255016512 0ustar www-datawww-data# frozen_string_literal: true require "rubygems/ext" require "shellwords" require_relative "cargo_builder" require_relative "mkmf/config" # rubocop:disable Style/GlobalVars # Root module module RbSys # Helper class for creating Rust Makefiles module Mkmf # @api private GLOBAL_RUSTFLAGS = ["--cfg=rb_sys_gem"] # Helper for building Rust extensions by creating a Ruby compatible makefile # for Rust. By using this class, your rust extension will be 100% compatible # with the rake-compiler gem, which allows for easy cross compilation. # # @example Basic # require 'mkmf' # require 'rb_sys/mkmf' # # create_rust_makefile("my_extension") #=> Generate a Makefile in the current directory # # @example Configure a custom build profile # require 'mkmf' # require 'rb_sys/mkmf' # # create_rust_makefile("my_extension") do |r| # # All of these are optional # r.env = { 'FOO' => 'bar' } # r.profile = ENV.fetch('RB_SYS_CARGO_PROFILE', :dev).to_sym # r.features = %w[some_cargo_feature] # r.rustflags = %w[--cfg=foo] # r.target_dir = "some/target/dir" # end def create_rust_makefile(target, &blk) if target.include?("/") target_prefix, target = File.split(target) target_prefix[0, 0] = "/" else target_prefix = "" end spec = Struct.new(:name, :metadata).new(target, {}) cargo_builder = CargoBuilder.new(spec) builder = Config.new(cargo_builder) yield builder if blk srcprefix = File.join("$(srcdir)", builder.ext_dir.gsub(/\A\.\/?/, "")).chomp("/") RbConfig.expand(srcdir = srcprefix.dup) full_cargo_command = cargo_command(srcdir, builder) global_rustflags = GLOBAL_RUSTFLAGS.dup global_rustflags << "--cfg=rb_sys_use_stable_api_compiled_fallback" if builder.use_stable_api_compiled_fallback? make_install = +<<~MAKE #{conditional_assign("RB_SYS_BUILD_DIR", File.join(Dir.pwd, ".rb-sys"))} #{conditional_assign("CARGO", "cargo")} #{conditional_assign("CARGO_BUILD_TARGET", builder.target)} #{conditional_assign("SOEXT", builder.so_ext)} #{try_load_bundled_libclang(builder)} # Determine the prefix Cargo uses for the lib. #{if_neq_stmt("$(SOEXT)", "dll")} #{conditional_assign("SOEXT_PREFIX", "lib", indent: 1)} #{endif_stmt} #{set_cargo_profile(builder)} #{conditional_assign("RB_SYS_CARGO_FEATURES", builder.features.join(","))} #{conditional_assign("RB_SYS_GLOBAL_RUSTFLAGS", global_rustflags.join(" "))} #{conditional_assign("RB_SYS_EXTRA_RUSTFLAGS", builder.extra_rustflags.join(" "))} #{conditional_assign("RB_SYS_EXTRA_CARGO_ARGS", builder.extra_cargo_args.join(" "))} #{conditional_assign("RB_SYS_CARGO_MANIFEST_DIR", builder.manifest_dir)} # Set dirname for the profile, since the profiles do not directly map to target dir (i.e. dev -> debug) #{if_eq_stmt("$(RB_SYS_CARGO_PROFILE)", "dev")} #{conditional_assign("RB_SYS_CARGO_PROFILE_DIR", "debug", indent: 1)} #{else_stmt} #{conditional_assign("RB_SYS_CARGO_PROFILE_DIR", "$(RB_SYS_CARGO_PROFILE)", indent: 1)} #{endif_stmt} # Set the build profile (dev, release, etc.). #{assign_stmt("RB_SYS_CARGO_PROFILE_FLAG", "--profile $(RB_SYS_CARGO_PROFILE)", indent: 1)} # Account for sub-directories when using `--target` argument with Cargo #{conditional_assign("RB_SYS_CARGO_TARGET_DIR", "target")} #{if_neq_stmt("$(CARGO_BUILD_TARGET)", "")} #{assign_stmt("RB_SYS_FULL_TARGET_DIR", "$(RB_SYS_CARGO_TARGET_DIR)/$(CARGO_BUILD_TARGET)", indent: 1)} #{else_stmt} #{assign_stmt("RB_SYS_FULL_TARGET_DIR", "$(RB_SYS_CARGO_TARGET_DIR)", indent: 1)} #{endif_stmt} target_prefix = #{target_prefix} TARGET_NAME = #{target[/\A\w+/]} TARGET_ENTRY = #{RbConfig::CONFIG["EXPORT_PREFIX"]}Init_$(TARGET_NAME) RUBYARCHDIR = $(sitearchdir)$(target_prefix) TARGET = #{target} DLLIB = $(TARGET).#{RbConfig::CONFIG["DLEXT"]} RUSTLIBDIR = $(RB_SYS_FULL_TARGET_DIR)/$(RB_SYS_CARGO_PROFILE_DIR) RUSTLIB = $(RUSTLIBDIR)/$(SOEXT_PREFIX)$(TARGET_NAME).$(SOEXT) TIMESTAMP_DIR = . CLEANOBJS = $(RUSTLIBDIR) $(RB_SYS_BUILD_DIR) CLEANLIBS = $(DLLIB) $(RUSTLIB) RUBYGEMS_CLEAN_DIRS = $(CLEANOBJS) $(CLEANFILES) #{builder.rubygems_clean_dirs.join(" ")} #{base_makefile(srcdir)} .PHONY: gemclean #{if_neq_stmt("$(RB_SYS_VERBOSE)", "")} #{assign_stmt("Q", "$(0=@)", indent: 1)} #{endif_stmt} #{env_vars(builder)} #{export_env("RUSTFLAGS", "$(RB_SYS_GLOBAL_RUSTFLAGS) $(RB_SYS_EXTRA_RUSTFLAGS) $(RUSTFLAGS)")} FORCE: ; #{optional_rust_toolchain(builder)} #{timestamp_file("sitearchdir")}: \t$(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR) \t$(Q) $(TOUCH) $@ $(RUSTLIB): FORCE \t$(ECHO) generating $(@) \\("$(RB_SYS_CARGO_PROFILE)"\\) \t#{full_cargo_command} $(DLLIB): $(RUSTLIB) \t$(Q) $(COPY) "$(RUSTLIB)" $@ install-so: $(DLLIB) #{timestamp_file("sitearchdir")} \t$(ECHO) installing $(DLLIB) to $(RUBYARCHDIR) \t$(Q) $(MAKEDIRS) $(RUBYARCHDIR) \t$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR) gemclean: \t$(ECHO) Cleaning gem artifacts \t-$(Q)$(RM_RF) $(RUBYGEMS_CLEAN_DIRS) 2> /dev/null || true install: #{builder.clean_after_install ? "install-so gemclean" : "install-so"} all: #{$extout ? "install" : "$(DLLIB)"} MAKE gsub_cargo_command!(make_install, builder: builder) File.write("Makefile", make_install) end private def base_makefile(cargo_dir) base_makefile = dummy_makefile(__dir__).join("\n") base_makefile.gsub!("all install static install-so install-rb", "all static install-rb") base_makefile.gsub!(/^srcdir = .*$/, "srcdir = #{cargo_dir}") base_makefile end def cargo_command(cargo_dir, builder) builder.ext_dir = cargo_dir dest_path = builder.target_dir || File.join(Dir.pwd, "target") args = ARGV.dup args.shift if args.first == "--" cargo_cmd = builder.cargo_command(dest_path, args) cmd = Shellwords.join(cargo_cmd) cmd.gsub!("\\=", "=") cmd.gsub!(/\Acargo rustc/, "$(CARGO) rustc $(RB_SYS_EXTRA_CARGO_ARGS) --manifest-path $(RB_SYS_CARGO_MANIFEST_DIR)/Cargo.toml") cmd.gsub!(/-v=\d/, "") cmd end def env_vars(builder) lines = [] if (cc = env_or_makefile_config("CC", builder)) && find_executable(cc) lines << assign_stmt("CC", cc) end if (cxx = env_or_makefile_config("CXX", builder)) && find_executable(cxx) lines << assign_stmt("CXX", cxx) end if (ar = env_or_makefile_config("AR", builder)) && find_executable(ar) lines << assign_stmt("AR", ar) end lines += builder.build_env.map { |k, v| env_line(k, v) } lines.compact.join("\n") end def env_line(k, v) return unless v export_env(k, strip_cmd(v.gsub("\n", '\n'))) end def strip_cmd(cmd) cmd.gsub("-nologo", "").strip end def env_or_makefile_config(key, builder) builder.env[key] || ENV[key] || RbConfig::MAKEFILE_CONFIG[key] end def gsub_cargo_command!(cargo_command, builder:) cargo_command.gsub!(/--profile \w+/, "$(RB_SYS_CARGO_PROFILE_FLAG)") cargo_command.gsub!(%r{--features \S+}, "--features $(RB_SYS_CARGO_FEATURES)") cargo_command.gsub!(%r{--target \S+}, "--target $(CARGO_BUILD_TARGET)") cargo_command.gsub!(/--target-dir (?:(?!--).)+/, "--target-dir $(RB_SYS_CARGO_TARGET_DIR) ") cargo_command end def rust_toolchain_env(builder) <<~MAKE #{conditional_assign("RB_SYS_RUSTUP_PROFILE", "minimal")} # If the user passed true, we assume stable Rust. Otherwise, use what # was specified (i.e. RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN=beta) #{if_eq_stmt("$(RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN)", "true")} RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN = stable #{endif_stmt} # If a $RUST_TARGET is specified (i.e. for rake-compiler-dock), append # that to the profile. #{if_eq_stmt("$(RUST_TARGET)", "")} RB_SYS_DEFAULT_TOOLCHAIN = $(RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN) #{else_stmt} RB_SYS_DEFAULT_TOOLCHAIN = $(RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN)-$(RUST_TARGET) #{endif_stmt} # Since we are forcing the installation of the Rust toolchain, we need # to set these env vars unconditionally for the build. #{export_env("CARGO_HOME", "$(RB_SYS_BUILD_DIR)/$(RB_SYS_DEFAULT_TOOLCHAIN)/cargo")} #{export_env("RUSTUP_HOME", "$(RB_SYS_BUILD_DIR)/$(RB_SYS_DEFAULT_TOOLCHAIN)/rustup")} #{export_env("PATH", "$(CARGO_HOME)/bin:$(RUSTUP_HOME)/bin:$(PATH)")} #{export_env("RUSTUP_TOOLCHAIN", "$(RB_SYS_DEFAULT_TOOLCHAIN)")} #{export_env("CARGO", "$(CARGO_HOME)/bin/cargo")} MAKE end def optional_rust_toolchain(builder) <<~MAKE #{conditional_assign("RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN", force_install_rust_toolchain?(builder))} # Only run if the we are told to explicitly install the Rust toolchain #{if_neq_stmt("$(RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN)", "false")} #{rust_toolchain_env(builder)} $(CARGO): \t$(Q) $(MAKEDIRS) $(CARGO_HOME) $(RUSTUP_HOME) \t$(Q) curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --no-modify-path --profile $(RB_SYS_RUSTUP_PROFILE) --default-toolchain none -y \t$(Q) $(CARGO_HOME)/bin/rustup toolchain install $(RB_SYS_DEFAULT_TOOLCHAIN) --profile $(RB_SYS_RUSTUP_PROFILE) \t$(Q) $(CARGO_HOME)/bin/rustup default $(RB_SYS_DEFAULT_TOOLCHAIN) #{install_extra_rustup_targets(builder)} $(RUSTLIB): $(CARGO) #{endif_stmt} MAKE end def install_extra_rustup_targets(builder) builder.extra_rustup_targets.map do |target| "\t$(Q) $(CARGO_HOME)/bin/rustup target add #{target}" end.join("\n") end def force_install_rust_toolchain?(builder) return builder.force_install_rust_toolchain if builder.force_install_rust_toolchain return false unless builder.rubygems_invoked? && builder.auto_install_rust_toolchain find_executable("cargo").nil? end def if_eq_stmt(a, b) if $nmake "!IF #{a.inspect} == #{b.inspect}" else "ifeq (#{a},#{b})" end end def if_neq_stmt(a, b) if $nmake "!IF #{a.inspect} != #{b.inspect}" else "ifneq (#{a},#{b})" end end def else_stmt if $nmake "!ELSE" else "else" end end def endif_stmt if $nmake "!ENDIF" else "endif" end end def conditional_assign(a, b, export: false, indent: 0) if $nmake result = +"!IFNDEF #{a}\n#{a} = #{b}\n!ENDIF\n" result << export_env(a, b) if export result else "#{"\t" * indent}#{export ? "export " : ""}#{a} ?= #{b}" end end def assign_stmt(a, b, indent: 0) if $nmake "#{a} = #{b}" else "#{"\t" * indent}#{a} = #{b}" end end def export_env(k, v) if $nmake "!if [set #{k}=#{v}]\n!endif" else "export #{k} := #{v}" end end def try_load_bundled_libclang(_builder) require "libclang" assert_libclang_version_valid! export_env("LIBCLANG_PATH", Libclang.libdir) rescue LoadError # If we can't load the bundled libclang, just continue end def assert_libclang_version_valid! libclang_version = Gem::Version.new(Libclang.version) if libclang_version < Gem::Version.new("5.0.0") raise "libclang version 5.0.0 or greater is required (current #{libclang_version})" end if libclang_version >= Gem::Version.new("17.0.0") raise "libclang version < 17.0.0 or greater is required (current #{libclang_version})" end end def set_cargo_profile(builder) return assign_stmt("RB_SYS_CARGO_PROFILE", "release") if builder.rubygems_invoked? conditional_assign("RB_SYS_CARGO_PROFILE", builder.profile) end end end # rubocop:enable Style/GlobalVars include RbSys::Mkmf # rubocop:disable Style/MixinUsage rb_sys-0.9.87/lib/rb_sys/util/0000755000004100000410000000000014557210255016202 5ustar www-datawww-datarb_sys-0.9.87/lib/rb_sys/util/logger.rb0000644000004100000410000000324114557210255020006 0ustar www-datawww-data# frozen_string_literal: true module RbSys module Util class Logger attr_accessor :io, :level def initialize(io: $stderr, level: :info) @io = ENV["GITHUB_ACTIONS"] ? $stdout : io @level = level end def error(message, **opts) add(:error, message, **opts) end def warn(message, **opts) add(:warn, message, **opts) end def info(message, **opts) add(:info, message, **opts) end def notice(message, **opts) add(:notice, message, **opts) end def trace(message, **opts) return unless level == :trace add(:trace, message, **opts) end def fatal(message, **opts) error(message, **opts) abort end private LEVEL_STYLES = { warn: ["⚠️", "\e[1;33m"], error: ["❌", "\e[1;31m"], notice: ["👋", "\e[1;37m"], info: ["🐳", "\e[1;34m"], trace: ["🔍", "\e[1;2m"] } if ENV["GITHUB_ACTIONS"] def add(level, message, emoji: true) emote, _ = LEVEL_STYLES.fetch(level.to_sym) io.puts "::#{level}::#{emote} #{message}" end else def add(level, message, emoji: true) emoji_opt, shellcode = LEVEL_STYLES.fetch(level.to_sym) emoji_opt = if emoji.is_a?(String) emoji + " " elsif emoji emoji_opt + " " end # Escape the message for bash shell codes (e.g. \033[1;31m) escaped = message.gsub("\\", "\\\\\\").gsub("\033", "\\033") io.puts "#{shellcode}#{emoji_opt}#{escaped}\033[0m" end end end end end rb_sys-0.9.87/lib/rb_sys/cargo_builder.rb0000644000004100000410000002370214557210255020357 0ustar www-datawww-datarequire "rubygems/ext" require "rubygems/ext/builder" require_relative "cargo_builder/link_flag_converter" module RbSys # A class to build a Ruby gem Cargo. Extracted from `rubygems` gem, with some modifications. # @api private class CargoBuilder < Gem::Ext::Builder WELL_KNOWN_WRAPPERS = %w[sccache cachepot].freeze attr_accessor :spec, :runner, :env, :features, :target, :extra_rustc_args, :dry_run, :ext_dir, :extra_rustflags, :extra_cargo_args attr_writer :profile def initialize(spec) require "rubygems/command" require_relative "cargo_builder/link_flag_converter" @spec = spec @runner = self.class.method(:run) @profile = ENV.fetch("RB_SYS_CARGO_PROFILE", :release).to_sym @env = {} @features = [] @target = ENV["CARGO_BUILD_TARGET"] || ENV["RUST_TARGET"] @extra_rustc_args = [] @extra_cargo_args = [] @dry_run = true @ext_dir = "" @extra_rustflags = [] end def profile return :release if rubygems_invoked? @profile end def build(_extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd) require "fileutils" require "shellwords" build_crate(dest_path, results, args, cargo_dir) validate_cargo_build!(dest_path) rename_cdylib_for_ruby_compatibility(dest_path) finalize_directory(dest_path, lib_dir, cargo_dir) results end def build_crate(dest_path, results, args, cargo_dir) env = build_env cmd = cargo_command(dest_path, args) runner.call cmd, results, "cargo", cargo_dir, env results end def build_env build_env = rb_config_env build_env["RUBY_STATIC"] = "true" if ruby_static? && ENV.key?("RUBY_STATIC") build_env.merge(env) end def manifest_dir ext_dir end def cargo_command(dest_path, args = []) cmd = [] cmd += ["cargo", "rustc"] cmd += ["--target", target] if target cmd += ["--target-dir", dest_path] cmd += ["--features", features.join(",")] unless features.empty? cmd += ["--lib"] cmd += ["--profile", profile.to_s] cmd += Gem::Command.build_args cmd += args cmd += ["--"] cmd += [*rustc_args(dest_path)] cmd += extra_rustc_args cmd end def cargo_dylib_path(dest_path) prefix = so_ext == "dll" ? "" : "lib" path_parts = [dest_path] path_parts << target if target path_parts += [profile_target_directory, "#{prefix}#{cargo_crate_name}.#{so_ext}"] File.join(*path_parts) end # We have to basically reimplement RbConfig::CONFIG['SOEXT'] here to support # Ruby < 2.5 # # @see https://github.com/ruby/ruby/blob/c87c027f18c005460746a74c07cd80ee355b16e4/configure.ac#L3185 def so_ext return RbConfig::CONFIG["SOEXT"] if RbConfig::CONFIG.key?("SOEXT") if win_target? "dll" elsif darwin_target? "dylib" else "so" end end private def rb_config_env result = {} RbConfig::CONFIG.each { |k, v| result["RBCONFIG_#{k}"] = v } result end def rustc_args(dest_dir) [ *linker_args, *mkmf_libpath, *rustc_dynamic_linker_flags(dest_dir), *rustc_lib_flags(dest_dir), *platform_specific_rustc_args(dest_dir) ] end def platform_specific_rustc_args(dest_dir, flags = []) if mingw_target? # On mingw platforms, mkmf adds libruby to the linker flags flags += libruby_args(dest_dir) # Make sure ALSR is used on mingw # see https://github.com/rust-lang/rust/pull/75406/files flags += ["-C", "link-arg=-Wl,--dynamicbase"] flags += ["-C", "link-arg=-Wl,--disable-auto-image-base"] # If the gem is installed on a host with build tools installed, but is # run on one that isn't the missing libraries will cause the extension # to fail on start. flags += ["-C", "link-arg=-static-libgcc"] elsif darwin_target? # See https://github.com/oxidize-rb/rb-sys/issues/88 dl_flag = "-Wl,-undefined,dynamic_lookup" flags += ["-C", "link-arg=#{dl_flag}"] unless makefile_config("DLDFLAGS")&.include?(dl_flag) end flags end # We want to use the same linker that Ruby uses, so that the linker flags from # mkmf work properly. def linker_args cc_flag = Shellwords.split(makefile_config("CC")) linker = cc_flag.shift if WELL_KNOWN_WRAPPERS.any? { |w| linker.include?(w) } linker = cc_flag.shift end link_args = cc_flag.flat_map { |a| ["-C", "link-arg=#{a}"] } return mswin_link_args if linker == "cl" ["-C", "linker=#{linker}", *link_args] end def mswin_link_args args = [] args += ["-l", makefile_config("LIBRUBYARG_SHARED").chomp(".lib")] args += split_flags("LIBS").flat_map { |lib| ["-l", lib.chomp(".lib")] } args += split_flags("LOCAL_LIBS").flat_map { |lib| ["-l", lib.chomp(".lib")] } args end def libruby_args(dest_dir) libs = makefile_config(ruby_static? ? "LIBRUBYARG_STATIC" : "LIBRUBYARG_SHARED") raw_libs = Shellwords.split(libs) raw_libs.flat_map { |l| ldflag_to_link_modifier(l) } end def ruby_static? return true if %w[1 true].include?(ENV["RUBY_STATIC"]) makefile_config("ENABLE_SHARED") == "no" end # Ruby expects the dylib to follow a file name convention for loading def rename_cdylib_for_ruby_compatibility(dest_path) new_path = final_extension_path(dest_path) FileUtils.cp(cargo_dylib_path(dest_path), new_path) new_path end def validate_cargo_build!(dir) dylib_path = cargo_dylib_path(dir) raise DylibNotFoundError, dir unless File.exist?(dylib_path) dylib_path end def final_extension_path(dest_path) dylib_path = cargo_dylib_path(dest_path) dlext_name = "#{spec.name}.#{makefile_config("DLEXT")}" dylib_path.gsub(File.basename(dylib_path), dlext_name) end def cargo_crate_name spec.metadata.fetch("cargo_crate_name", spec.name).tr("-", "_") end def rustc_dynamic_linker_flags(dest_dir) split_flags("DLDFLAGS") .map { |arg| maybe_resolve_ldflag_variable(arg, dest_dir) } .compact .flat_map { |arg| ldflag_to_link_modifier(arg) } end def rustc_lib_flags(dest_dir) split_flags("LIBS").flat_map { |arg| ldflag_to_link_modifier(arg) } end def split_flags(var) Shellwords.split(RbConfig::CONFIG.fetch(var, "")) end def ldflag_to_link_modifier(arg) LinkFlagConverter.convert(arg) end def msvc_target? makefile_config("target_os").include?("msvc") end def darwin_target? makefile_config("target_os").include?("darwin") end def mingw_target? makefile_config("target_os").include?("mingw") end def win_target? target_platform = RbConfig::CONFIG["target_os"] !!Gem::WIN_PATTERNS.find { |r| target_platform =~ r } end # Interpolate substition vars in the arg def maybe_resolve_ldflag_variable(input_arg, dest_dir) var_matches = input_arg.match(/\$\((\w+)\)/) return input_arg unless var_matches var_name = var_matches[1] return input_arg if var_name.nil? || var_name.chomp.empty? case var_name when "DEFFILE" # DEFFILE already generated by cargo else RbConfig::CONFIG[var_name] end end # Corresponds to $(LIBPATH) in mkmf def mkmf_libpath ["-L", "native=#{makefile_config("libdir")}"] end def makefile_config(var_name) val = RbConfig::MAKEFILE_CONFIG[var_name] return unless val RbConfig.expand(val.dup) end # Copied from ExtConfBuilder def finalize_directory(dest_path, lib_dir, extension_dir) require "fileutils" require "tempfile" ext_path = final_extension_path(dest_path) begin tmp_dest = Dir.mktmpdir(".gem.", extension_dir) # Some versions of `mktmpdir` return absolute paths, which will break make # if the paths contain spaces. However, on Ruby 1.9.x on Windows, relative # paths cause all C extension builds to fail. # # As such, we convert to a relative path unless we are using Ruby 1.9.x on # Windows. This means that when using Ruby 1.9.x on Windows, paths with # spaces do not work. # # Details: https://github.com/rubygems/rubygems/issues/977#issuecomment-171544940 tmp_dest_relative = get_relative_path(tmp_dest.clone, extension_dir) if tmp_dest_relative full_tmp_dest = File.join(extension_dir, tmp_dest_relative) # TODO: remove in RubyGems 3 if Gem.install_extension_in_lib && lib_dir FileUtils.mkdir_p lib_dir FileUtils.cp_r ext_path, lib_dir, remove_destination: true end FileUtils::Entry_.new(full_tmp_dest).traverse do |ent| destent = ent.class.new(dest_path, ent.rel) destent.exist? || FileUtils.mv(ent.path, destent.path) end end ensure FileUtils.rm_rf tmp_dest if tmp_dest end end def get_relative_path(path, base) path[0..base.length - 1] = "." if path.start_with?(base) path end def profile_target_directory case profile.to_sym when :release then "release" when :dev then "debug" else raise "unknown target directory for profile: #{profile}" end end def rubygems_invoked? ENV.key?("SOURCE_DATE_EPOCH") end # Error raised when no cdylib artifact was created class DylibNotFoundError < StandardError def initialize(dir) files = Dir.glob(File.join(dir, "**", "*")).map { |f| "- #{f}" }.join "\n" super <<~MSG Dynamic library not found for Rust extension (in #{dir}) Make sure you set "crate-type" in Cargo.toml to "cdylib" Found files: #{files} MSG end end end end rb_sys-0.9.87/lib/rb_sys/version.rb0000644000004100000410000000010514557210255017233 0ustar www-datawww-data# frozen_string_literal: true module RbSys VERSION = "0.9.87" end rb_sys-0.9.87/lib/rb_sys/toolchain_info.rb0000644000004100000410000000424114557210255020546 0ustar www-datawww-data# frozen_string_literal: true require_relative "toolchain_info/data" module RbSys # A class to get information about the Rust toolchains, and how they map to # Ruby platforms. # # @example # RbSys::ToolchainInfo.new("x86_64-unknown-linux-gnu").ruby_platform # => "x86_64-linux" # RbSys::ToolchainInfo.new("x86_64-unknown-linux-gnu").supported? # => true # RbSys::ToolchainInfo.new("x86_64-unknown-linux-gnu") class ToolchainInfo attr_reader :platform, :gem_platform, :rust_target, :rake_compiler_dock_cc, :supported, :rake_compiler_dock_image, :docker_platform class << self # Get all known toolchains. # # @return [Array] def all @all ||= DATA.keys.map { |k| new(k) } end # Get all supported toolchains. # # @return [Array] def supported @supported ||= all.select(&:supported?) end # Get the toolchain for the current platform. # # @return [RbSys::ToolchainInfo] def local @current ||= new(RbConfig::CONFIG["arch"]) end end # Create a new toolchain info object. # # @param platform [String] The platform to get information about. def initialize(platform) @platform = platform @gem_platform = Gem::Platform.new(platform) data = DATA[platform] || DATA["#{gem_platform.cpu}-#{gem_platform.os}"] || raise(ArgumentError, "unknown ruby platform: #{platform.inspect}") @rust_target = data["rust-target"] @rake_compiler_dock_cc = data["rake-compiler-dock"]["cc"] @supported = data["supported"] @rake_compiler_dock_image = "rbsys/#{platform}:#{RbSys::VERSION}" @docker_platform = data["docker-platform"] end # Whether this toolchain is supported. # # @return [Boolean] def supported? @supported end # String representation of the toolchain. # # @return [String] def to_s "#{gem_platform.cpu}-#{gem_platform.os}" end # Compare two toolchains. # # @param other [RbSys::ToolchainInfo] # @return [Boolean] def ==(other) @gem_platform == other.gem_platform end end end rb_sys-0.9.87/lib/rb_sys/mkmf/0000755000004100000410000000000014557210255016157 5ustar www-datawww-datarb_sys-0.9.87/lib/rb_sys/mkmf/config.rb0000644000004100000410000000375714557210255017765 0ustar www-datawww-data# Root module module RbSys # Helper class for creating Rust Makefiles module Mkmf # Config that delegates to CargoBuilder if needded class Config # Force the installation of the Rust toolchain when building attr_accessor :force_install_rust_toolchain # Clean artifacts after install (default: true if invoked by Rubygems) attr_accessor :clean_after_install # Target directory for cargo artifacts attr_accessor :target_dir # Automatically install the Rust toolchain when building (default: true) attr_accessor :auto_install_rust_toolchain # Directories to clean after installing with Rubygems attr_accessor :rubygems_clean_dirs # Extra targets to install attr_accessor :extra_rustup_targets # Use compiled C code fallback for stable API for ruby-head (default: false) attr_accessor :use_stable_api_compiled_fallback def initialize(builder) @builder = builder @force_install_rust_toolchain = false @auto_install_rust_toolchain = true @use_stable_api_compiled_fallback = false @clean_after_install = rubygems_invoked? @rubygems_clean_dirs = ["./cargo-vendor"] @extra_rustup_targets = [] end # @api private def cross_compiling? RbConfig::CONFIG["CROSS_COMPILING"] == "yes" end # @api private def method_missing(name, *args, &blk) @builder.send(name, *args, &blk) end # @api private def respond_to_missing?(name, include_private = false) @builder.respond_to?(name) || super end # Seems to be the only way to reliably know if we were invoked by Rubygems. # We want to know this so we can cleanup the target directory after an # install, to remove bloat. # @api private def rubygems_invoked? ENV.key?("SOURCE_DATE_EPOCH") end def use_stable_api_compiled_fallback? @use_stable_api_compiled_fallback end end end end rb_sys-0.9.87/lib/rb_sys/extensiontask.rb0000644000004100000410000001124514557210255020454 0ustar www-datawww-datarequire_relative "cargo/metadata" require_relative "error" begin require "rake/extensiontask" rescue LoadError abort "Please install rake-compiler to use this feature" end module RbSys # ExtensionTask is a Rake::ExtensionTask subclass that is used to tailored for # Rust extensions. It has the same options a `Rake::ExtensionTask`. # # @see https://www.rubydoc.info/gems/rake-compiler/Rake/ExtensionTask # # @example # RbSys::ExtensionTask.new("my-crate", my_gemspec) do |ext| # ext.lib_dir = "lib/my-crate" # end # # @param name [String] the crate name to build # @param gem_spec [Gem::Specification] the gem specification to build (needed for cross-compiling) # @return [Rake::ExtensionTask] class ExtensionTask < Rake::ExtensionTask def initialize(name = nil, gem_spec = :undefined) super end def init(name = nil, gem_spec = nil) super(name, lint_gem_spec(name, gem_spec)) @orginal_ext_dir = @ext_dir @ext_dir = cargo_metadata.manifest_directory @source_pattern = nil @compiled_pattern = "*.{obj,so,bundle,dSYM}" @cross_compile = ENV.key?("RUBY_TARGET") @cross_platform = [ENV["RUBY_TARGET"]].compact @cross_compiling_blocks = [] @cross_compiling_blocks << proc do |gemspec| warn "Removing unneeded dependencies from native gemspec" gemspec.dependencies.reject! { |d| d.name == "rb_sys" } end @cross_compiling_blocks << proc do |gemspec| warn "Removing source files from native gemspec" gemspec.files.reject! { |f| f.end_with?(".rs") } gemspec.files.reject! { |f| f.match?(/Cargo.(toml|lock)$/) } gemspec.files.reject! { |f| extconf.end_with?(f) } gemspec.extensions.reject! { |f| f.end_with?("Cargo.toml") } end end def define super define_env_tasks CLEAN.include(target_directory) if defined?(CLEAN) end def cargo_metadata @cargo_metadata ||= Cargo::Metadata.new(@name) end def extconf File.join(cargo_metadata.manifest_directory, "extconf.rb") end def binary(_platf) super.tr("-", "_") end # I'm not sure why this is necessary, can it be removed? def source_files list = FileList[ "#{ext_dir}/**/*.{rs,rb,c,h,toml}", "**/Cargo.{toml,lock}", "**/.cargo/**/*", "#{ext_dir}/lib/**/*" ] list.include("#{ext_dir}/#{@source_pattern}") if @source_pattern list.exclude(File.join(target_directory, "**/*")) list end def cross_compiling(&block) @cross_compiling_blocks << block if block end def target_directory cargo_metadata.target_directory end def define_native_tasks(for_platform = nil, ruby_ver = RUBY_VERSION, callback = nil) cb = proc do |gemspec| callback&.call(gemspec) @cross_compiling_blocks.each do |block| block.call(gemspec) end end super(for_platform, ruby_ver, cb) end def define_env_tasks task "rb_sys:env:default" do ENV["RB_SYS_CARGO_TARGET_DIR"] ||= target_directory ENV["RB_SYS_CARGO_MANIFEST_DIR"] ||= cargo_metadata.manifest_directory ENV["RB_SYS_CARGO_PROFILE"] ||= "release" end desc "Use the debug profile for building native Rust extensions" task "rb_sys:env:dev" do ENV["RB_SYS_CARGO_PROFILE"] = "dev" end desc "Use the release profile for building native Rust extensions" task "rb_sys:env:release" do ENV["RB_SYS_CARGO_PROFILE"] = "release" end file extconf => "rb_sys:env:default" desc 'Compile the native Rust extension with the "dev" profile' task "compile:dev" => ["rb_sys:env:dev", "compile"] desc 'Compile the native Rust extension with the "release" profile' task "compile:release" => ["rb_sys:env:release", "compile"] end private def lint_gem_spec(name, gs) gem_spec = case gs when :undefined return when Gem::Specification gs when String Gem::Specification.load(gem_spec) || raise(ArgumentError, "Unable to load gemspec from file #{gs.inspect}") else raise ArgumentError, "gem_spec must be a Gem::Specification, got #{gs.class}" end gem_spec.files.each do |f| if /\.(dll|so|dylib|lib|bundle)$/.match?(f) warn "⚠️ gemspec includes native artifact (#{f}), please remove it." end end if (gem_crate_name = gem_spec.metadata["cargo_crate_name"]) if name != gem_crate_name warn "⚠️ cargo_crate_name (#{gem_crate_name}) does not match extension task crate name (#{name})" end end gem_spec end end end rb_sys-0.9.87/.yardopts0000644000004100000410000000005014557210255015017 0ustar www-datawww-data--no-private --list-undoc 'lib/**/*.rb' rb_sys-0.9.87/checksums.yaml.gz.sig0000444000004100000410000000040014557210255017217 0ustar www-datawww-datahdj"A]};3v9fP_9a`fL'՜G) ؐ~1f, ?-BW#KD"Q:5㺡oM|W䖖:]3yk;PGL-Ee;X')cyefMlowEݛϜ~̓$RuPZZ5/fB\bģFJ=]ƔLA4[p6ݻ:rb_sys-0.9.87/rb_sys.gemspec0000644000004100000410000000600214557210256016023 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: rb_sys 0.9.87 ruby lib Gem::Specification.new do |s| s.name = "rb_sys".freeze s.version = "0.9.87" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "homepage_uri" => "https://oxidize-rb.github.io/rb-sys/", "rubygems_mfa_required" => "true", "source_code_uri" => "https://github.com/oxidize-rb/rb-sys" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Ian Ker-Seymer".freeze] s.bindir = "exe".freeze s.cert_chain = ["-----BEGIN CERTIFICATE-----\nMIIDfDCCAmSgAwIBAgIBATANBgkqhkiG9w0BAQsFADBCMRQwEgYDVQQDDAtpLmtl\ncnNleW1lcjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYD\nY29tMB4XDTIzMDQxNDEzMzYxNVoXDTI0MDQxMzEzMzYxNVowQjEUMBIGA1UEAwwL\naS5rZXJzZXltZXIxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixk\nARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMJ2pG+er4cP\nPasxsMIKL9/tmLL4gh80EMuF3SCS0qZoh+Oo8dkvRYxW8NXdwEIcp3cCNgE+5G+J\nTCMOVF8S15n1Z1P7xxXiXxa/BIofKKbtatVRngm14uR/6pjdkvLXqlrWdS57bNwv\n7LtpzYVfDHfsl/qRWaEi4jq00PNCRSWjcva8teqswjBg8KlwGtyygpezPbVSWP8Y\nvzWZmVF7fqRBXU78Ah0+pNOhslBXDTvI3xJdN4hQ3H7rLjpD/qxKWq/8o+Qvx6cX\ndNZ3ugH/Pr3BAsqt4JFLXin9AK7PO9GDMH5JXJrUb+hAt2VNIZqpz9VlKA6BA0jN\neWGea+yCZkECAwEAAaN9MHswCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O\nBBYEFOkrF6hsocaIMOjR/K3JBzyXCLJPMCAGA1UdEQQZMBeBFWkua2Vyc2V5bWVy\nQGdtYWlsLmNvbTAgBgNVHRIEGTAXgRVpLmtlcnNleW1lckBnbWFpbC5jb20wDQYJ\nKoZIhvcNAQELBQADggEBAMAohCl0cdVlYrJl9viGtzbEyLV/Krn0ZD/LM9d2cIRj\nWBu7HRkP6AfYVzhZwdgUgDYw67d715kOMpLNGeWQw3QGEHFqbVzRML64jL8IxuTz\nJ8ttZVqM8f4GrHyISJnL92u6iP4WzdbVqx89EFjrrHd14OJXK+ZpdRh0YvnN/vMJ\nt2EcDweRHkN/MEdrwbH+PbYjELnKcTIEZqcCWTk8pgssGrNyjkMhxGSsSUo9qTz0\nDIZ6NVmpBvohJVCCCDxQQxFKLXZp1ivoxjN+m7eJSW7yzIz062pH4u8pPNQsiVSb\nI5rgRPbDr2rAFGXKoQ0+u6CLkRxqrVsITl/OPfZhBQI=\n-----END CERTIFICATE-----\n".freeze] s.date = "2024-01-20" s.email = ["i.kerseymer@gmail.com".freeze] s.executables = ["rb-sys-dock".freeze] s.files = [".yardopts".freeze, "LICENSE-APACHE".freeze, "LICENSE-MIT".freeze, "README.md".freeze, "certs/ianks.pem".freeze, "exe/rb-sys-dock".freeze, "lib/rb_sys.rb".freeze, "lib/rb_sys/cargo.rb".freeze, "lib/rb_sys/cargo/metadata.rb".freeze, "lib/rb_sys/cargo_builder.rb".freeze, "lib/rb_sys/cargo_builder/link_flag_converter.rb".freeze, "lib/rb_sys/error.rb".freeze, "lib/rb_sys/extensiontask.rb".freeze, "lib/rb_sys/mkmf.rb".freeze, "lib/rb_sys/mkmf/config.rb".freeze, "lib/rb_sys/toolchain_info.rb".freeze, "lib/rb_sys/toolchain_info/data.rb".freeze, "lib/rb_sys/util/logger.rb".freeze, "lib/rb_sys/version.rb".freeze] s.homepage = "https://oxidize-rb.github.io/rb-sys/".freeze s.licenses = ["MIT".freeze, "Apache-2.0".freeze] s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze) s.rubygems_version = "3.3.15".freeze s.summary = "Helpers for compiling Rust extensions for ruby".freeze end rb_sys-0.9.87/data.tar.gz.sig0000444000004100000410000000040014557210255015767 0ustar www-datawww-data^/^D'DIj./s˜)VVk/P)&:P<.ӣsb=ϳѾ֪]++^ \k-. iW+S4&OUO{FQ H?y&N%DY E{LAFBPk7V #ރ?:v(GN 㵺ϧ˦-VWƝsRpROrb_sys-0.9.87/LICENSE-APACHE0000644000004100000410000002501414557210255015104 0ustar www-datawww-data Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2021-2022 Ian Ker-Seymer Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. rb_sys-0.9.87/metadata.gz.sig0000444000004100000410000000040014557210255016051 0ustar www-datawww-dataޝ kpreƃՖ2A&PAvͽXp5#8l]+r&@/'he"6fO+)ްxxr7T;`޵tb\b`+8AU{[kLx ᝩ{:a2Aon"4E*yV3wۡ2*E橔BiQY}v 쳌YĀ{-ݧlLqrb_sys-0.9.87/README.md0000644000004100000410000000772414557210255014447 0ustar www-datawww-data# The `rb_sys` gem ![Gem](https://img.shields.io/gem/v/rb_sys) [![Documentation](https://img.shields.io/badge/docs-rdoc.info-blue.svg)](https://www.rubydoc.info/gems/rb_sys/frames) [![Join the discussion](https://img.shields.io/badge/slack-chat-blue.svg)](https://join.slack.com/t/oxidize-rb/shared_invite/zt-16zv5tqte-Vi7WfzxCesdo2TqF_RYBCw) The `rb_sys` gem is a Ruby gem makes it easy to build native Ruby extensions in Rust. It interops with the existing Ruby native extension toolchains (i.e. `rake-compiler`) to make testing, building, and cross compilation of gems easy. ## `RbSys::ExtensionTask` This gem provides a `RbSys::ExtensionTask` class that can be used to build a Ruby extension in Rust. It's a thin wrapper around `Rake::ExtensionTask` that provides sane defaults for building Rust extensions. ```ruby # Rakefile require "rb_sys/extensiontask" GEMSPEC = Gem::Specification.load("my_gem.gemspec") RbSys::ExtensionTask.new("my-crate-name", GEMSPEC) do |ext| ext.lib_dir = "lib/my_gem" # If you want to use `rb-sys-dock` for cross-compilation: ext.cross_compile = true end ``` ## `create_rust_makefile` This gem provides a simple helper to build a Ruby compatible Makefile for you Rust extension. For a full example, see the [examples](./../examples) directory. ```ruby # ext/rust_reverse/extconf.rb # We need to require mkmf *first* this since `rake-compiler` injects code here for cross compilation require "mkmf" require "rb_sys/mkmf" create_rust_makefile("rust_reverse") do |r| # Create debug builds in dev. Make sure that release gems are compiled with # `RB_SYS_CARGO_PROFILE=release` (optional) r.profile = ENV.fetch("RB_SYS_CARGO_PROFILE", :dev).to_sym # Can be overridden with `RB_SYS_CARGO_FEATURES` env var (optional) r.features = ["test-feature"] # You can add whatever env vars you want to the env hash (optional) r.env = {"FOO" => "BAR"} # If your Cargo.toml is in a different directory, you can specify it here (optional) r.ext_dir = "." # Extra flags to pass to the $RUSTFLAGS environment variable (optional) r.extra_rustflags = ["--cfg=some_nested_config_var_for_crate"] # Force a rust toolchain to be installed via rustup (optional) # You can also set the env var `RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN=true` r.force_install_rust_toolchain = "stable" # Clean up the target/ dir after `gem install` to reduce bloat (optional) r.clean_after_install = false # default: true if invoked by rubygems # Auto-install Rust toolchain if not present on "gem install" (optional) r.auto_install_rust_toolchain = false # default: true if invoked by rubygems end ``` ## Tips and Tricks - When using `rake-compiler` to build your gem, you can use the `RB_SYS_CARGO_PROFILE` environment variable to set the Cargo profile (i.e. `release` or `dev`). - You can pass Cargo arguments to `rake-compiler` like so: `rake compile -- --verbose` - It's possible to force an installation of a Rust toolchain by setting the `RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN` environment variable. This will install [rustup](https://rustup.rs/) and [cargo](https://crates.io/) in the build directory, so the end user does not have to have Rust pre-installed. Ideally, this should be a last resort, as it's better to already have the toolchain installed on your system. ## Troubleshooting ### Libclang issues If you see an error like this: ``` thread 'main' panicked at 'Unable to find libclang: "couldn't find any valid shared libraries matching: \['libclang.so', 'libclang-*.so', 'libclang.so.*', 'libclang-*.so.*'\], set the `LIBCLANG_PATH` environment variable to a path where one of these files can be found (invalid: \[\])"' ``` This means that bindgen is having issues finding a usable version of libclang. An easy way to fix this is to install the [`libclang` gem](https://github.com/oxidize-rb/libclang-rb), which will install a pre-built version of libclang for you. `rb_sys` will automatically detect this gem and use it. ```ruby # Gemfile gem "libclang", "~> 14.0.6" ```