Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Ruby namespace #100

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ appveyor = { repository = "tildeio/helix", branch = "master", service = "github"

[workspace]

members = ["examples/calculator", "examples/console", "examples/duration", "examples/membership", "examples/text_transform", "examples/turbo_blank"]
members = ["examples/primitive", "examples/calculator", "examples/console", "examples/duration", "examples/membership", "examples/text_transform", "examples/turbo_blank"]

[dependencies]
libc = "0.2.0"
Expand Down
34 changes: 34 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
FROM bitnami/minideb:jessie

RUN apt-get update
RUN apt-get install curl -y --no-install-recommends
RUN apt-get install ca-certificates -y --no-install-recommends
RUN apt-get install wget tar -y --no-install-recommends
RUN apt-get install build-essential make -y --no-install-recommends
RUN apt-get install build-essential git -y --no-install-recommends

RUN adduser --disabled-password --gecos '' helix
USER helix

RUN curl https://sh.rustup.rs -sSf | sh -s -- -y

ENV PATH="/home/helix/.cargo/bin:${PATH}"

RUN rustup target add x86_64-unknown-linux-musl

RUN cd ~ && wget -O ruby-install-0.6.1.tar.gz https://github.com/postmodern/ruby-install/archive/v0.6.1.tar.gz
RUN cd ~ && tar -xzvf ruby-install-0.6.1.tar.gz

USER root

RUN cd /home/helix/ruby-install-0.6.1 && make install
RUN ruby-install ruby 2.4.1 --system

USER helix
ENV GEM_HOME=/home/helix/.gem
ENV GEM_PATH=/home/helix/.gem
ENV PATH="/home/helix/.gem/bin:${PATH}"
RUN gem install bundler rake --no-ri --no-rdoc

VOLUME "app"
WORKDIR "/app"
106 changes: 54 additions & 52 deletions crates/libcruby-sys/build.rs
Original file line number Diff line number Diff line change
@@ -1,65 +1,67 @@
use std::{env,fs};
use std::path::Path;
use std::process::Command;
// use std::{env,fs};
// use std::path::Path;
// use std::process::Command;

fn main() {
// TODO: Clean this all up. There has to be a prettier way.
let target = env::var("TARGET").expect("TARGET required");
let manifest_dir_str = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR required");
let version_str = env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION required").replace(".", "-");
fn main() {}

let root = Path::new(manifest_dir_str.as_str());
// fn main() {
// // TODO: Clean this all up. There has to be a prettier way.
// let target = env::var("TARGET").expect("TARGET required");
// let manifest_dir_str = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR required");
// let version_str = env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION required").replace(".", "-");

let lib_root_str = env::var("HELIX_LIB_DIR").unwrap_or(manifest_dir_str.clone());
let lib_root = Path::new(lib_root_str.as_str());
// let root = Path::new(manifest_dir_str.as_str());

// Best way I could find to tell if we're packaging vs just building
let is_packaging = root.parent().expect("root has no parent").ends_with("target/package");
let libname32 = format!("helix-runtime-{}.i386", version_str);
let libname64 = format!("helix-runtime-{}.x86_64", version_str);
let libname = if target.starts_with("x86_64") { libname64.clone() } else { libname32.clone() };
// let lib_root_str = env::var("HELIX_LIB_DIR").unwrap_or(manifest_dir_str.clone());
// let lib_root = Path::new(lib_root_str.as_str());

// Not required for non-Windows, but it needs to be part of the package
if is_packaging && (!lib_root.join(format!("{}.lib", libname32)).exists() ||
!lib_root.join(format!("{}.lib", libname64)).exists()) {
panic!("{}.lib and {}.lib must exist when packaging. Please run ./prepackage.sh and set HELIX_LIB_DIR.", libname32, libname64);
}
// // Best way I could find to tell if we're packaging vs just building
// let is_packaging = root.parent().expect("root has no parent").ends_with("target/package");
// let libname32 = format!("helix-runtime-{}.i386", version_str);
// let libname64 = format!("helix-runtime-{}.x86_64", version_str);
// let libname = if target.starts_with("x86_64") { libname64.clone() } else { libname32.clone() };

if target.contains("windows") && !lib_root.join(format!("{}.lib", libname)).exists() {
panic!("{}.lib must exist when running. Set HELIX_LIB_DIR to ruby/windows_build for development.", libname);
}
// // Not required for non-Windows, but it needs to be part of the package
// if is_packaging && (!lib_root.join(format!("{}.lib", libname32)).exists() ||
// !lib_root.join(format!("{}.lib", libname64)).exists()) {
// panic!("{}.lib and {}.lib must exist when packaging. Please run ./prepackage.sh and set HELIX_LIB_DIR.", libname32, libname64);
// }

if target.contains("windows") {
let out_dir_str = env::var("OUT_DIR").expect("OUT_DIR required");
// if target.contains("windows") && !lib_root.join(format!("{}.lib", libname)).exists() {
// panic!("{}.lib must exist when running. Set HELIX_LIB_DIR to ruby/windows_build for development.", libname);
// }

let out_dir = Path::new(out_dir_str.as_str());
// if target.contains("windows") {
// let out_dir_str = env::var("OUT_DIR").expect("OUT_DIR required");

// Read info about current Ruby install
let raw_ruby_info = Command::new("ruby")
.arg(root.join("ruby_info.rb"))
.output()
.expect("failed to get Ruby info");
let raw_ruby_output = String::from_utf8_lossy(&raw_ruby_info.stdout);
let mut raw_ruby_lines = raw_ruby_output.lines();
let ruby_libdir = Path::new(raw_ruby_lines.next().expect("Ruby info has no libdir"));
let libruby = raw_ruby_lines.next().expect("Ruby info has no LIBRUBY");
let libruby_so = raw_ruby_lines.next().expect("Ruby info has no LIBRUBY_SO");
if raw_ruby_lines.next() != None {
panic!("Unexpected information returned in Ruby info");
}
// let out_dir = Path::new(out_dir_str.as_str());

let ruby_libname = libruby_so.split('.').next().expect("can't extract Ruby lib name");
// // Read info about current Ruby install
// let raw_ruby_info = Command::new("ruby")
// .arg(root.join("ruby_info.rb"))
// .output()
// .expect("failed to get Ruby info");
// let raw_ruby_output = String::from_utf8_lossy(&raw_ruby_info.stdout);
// let mut raw_ruby_lines = raw_ruby_output.lines();
// let ruby_libdir = Path::new(raw_ruby_lines.next().expect("Ruby info has no libdir"));
// let libruby = raw_ruby_lines.next().expect("Ruby info has no LIBRUBY");
// let libruby_so = raw_ruby_lines.next().expect("Ruby info has no LIBRUBY_SO");
// if raw_ruby_lines.next() != None {
// panic!("Unexpected information returned in Ruby info");
// }

// Copy .dll.a file to .lib since Rust msvc looks for .lib files only
fs::copy(ruby_libdir.join(libruby), out_dir.join(ruby_libname).with_extension("lib"))
.expect("unable to copy libruby");
// let ruby_libname = libruby_so.split('.').next().expect("can't extract Ruby lib name");

// Set up linker
println!("cargo:rustc-flags=-L {libpath} -l dylib={libruby} -L {root} -l helix-runtime:{libname}",
libpath=out_dir.to_str().expect("can't get str from out_dir"),
libruby=ruby_libname,
root=lib_root.to_str().expect("can't get str from root dir"),
libname=libname);
}
}
// // Copy .dll.a file to .lib since Rust msvc looks for .lib files only
// fs::copy(ruby_libdir.join(libruby), out_dir.join(ruby_libname).with_extension("lib"))
// .expect("unable to copy libruby");

// // Set up linker
// println!("cargo:rustc-flags=-L {libpath} -l dylib={libruby} -L {root} -l helix-runtime:{libname}",
// libpath=out_dir.to_str().expect("can't get str from out_dir"),
// libruby=ruby_libname,
// root=lib_root.to_str().expect("can't get str from root dir"),
// libname=libname);
// }
// }

2 changes: 2 additions & 0 deletions crates/libcruby-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ extern "C" {
#[link_name = "HELIX_T_BIGNUM"]
pub static T_BIGNUM: isize;

pub fn rb_ary_aref(obj: VALUE, offset: libc::c_long) -> VALUE;

// unknown if working?
// fn rb_define_variable(name: c_string, value: *const VALUE);
pub fn rb_obj_class(obj: VALUE) -> VALUE;
Expand Down
14 changes: 10 additions & 4 deletions examples/membership/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,19 @@ ruby! {
}
}

// Delete me:
// This is incredibly terrible and illustrates an increasingly bad problem
// with the current factoring around reopen. TLDR: reopen really doesn't
// work at the moment and you shouldn't use it.

use helix::{UncheckedValue, ToRust};
use helix::{UncheckedValue, ToRust, ruby};
use helix::coercions::CallFrame;

impl AsRef<[usize]> for Array {
fn as_ref(&self) -> &[usize] {
let checked = self.helix.to_checked().unwrap();
checked.to_rust()
let lt: &'static () = unsafe { std::mem::transmute(&()) };
let frame = unsafe { CallFrame::new(lt) };
let val = unsafe { ruby::Value::new(self.helix, frame) };
let checked = UncheckedValue::<&[usize]>::to_checked(val).unwrap();
ToRust::<&[usize]>::to_rust(checked)
}
}
3 changes: 3 additions & 0 deletions examples/primitive/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
target
*.bundle
*.gem
11 changes: 11 additions & 0 deletions examples/primitive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "primitive"
version = "0.1.0"
authors = ["Peter Wagenet <peter@tilde.io>"]

[lib]

crate-type = ["cdylib"]

[dependencies.helix]
path = "../.."
5 changes: 5 additions & 0 deletions examples/primitive/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
source 'https://rubygems.org'

gem 'helix_runtime', path: '../../ruby'
gem 'rake', '~> 10.0'
gem 'rspec', '~> 3.4'
39 changes: 39 additions & 0 deletions examples/primitive/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
PATH
remote: ../../ruby
specs:
helix_runtime (0.5.0)
rake (>= 10.0)
thor (~> 0.19.4)

GEM
remote: https://rubygems.org/
specs:
diff-lcs (1.3)
rake (10.5.0)
rspec (3.5.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-core (3.5.4)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
thor (0.19.4)

PLATFORMS
ruby
x64-mingw32
x86-mingw32

DEPENDENCIES
helix_runtime!
rake (~> 10.0)
rspec (~> 3.4)

BUNDLED WITH
1.14.6
20 changes: 20 additions & 0 deletions examples/primitive/Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require 'bundler/setup'
require 'helix_runtime/build_task'
require 'rspec/core/rake_task'
require_relative '../shared.rb'

# For Windows
$stdout.sync = true

HelixRuntime::BuildTask.new("primitive") do |t|
t.build_root = File.expand_path("../..", __dir__)
t.helix_lib_dir = File.expand_path("../../ruby/windows_build", __dir__)
t.pre_build = HelixRuntime::Tests.pre_build
end

RSpec::Core::RakeTask.new(:spec) do |t|
t.verbose = false
end

task :spec => :build
task :default => :spec
2 changes: 2 additions & 0 deletions examples/primitive/lib/primitive.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require 'helix_runtime'
require 'primitive/native'
43 changes: 43 additions & 0 deletions examples/primitive/spec/primitive_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require "spec_helper"

describe Primitive do
describe "#is_bool" do
cases = {
true => true,
false => true,
nil => false,
"true" => false,
"" => false,
0 => false,
0.5 => false,
{} => false,
Object.new => false
}

cases.each do |test, expected|
it "#{test.inspect} => #{expected}" do
expect(Primitive.is_bool(test)).to eq(expected)
end
end
end

describe "#as_bool" do
it "true" do
expect(Primitive.as_bool(true)).to eq(true)
end

it "nil" do
expect(-> { Primitive.as_bool(nil) }).to raise_error(RuntimeError)
end
end

describe "#first" do
it "[1, 2, 3]" do
expect(Primitive.first([1,2,3])).to eq(1)
end

it "['a', 2, 'c']" do
expect(Primitive.first(['a', 2, 'c'])).to eq('a')
end
end
end
2 changes: 2 additions & 0 deletions examples/primitive/spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
require 'primitive'
19 changes: 19 additions & 0 deletions examples/primitive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#[macro_use]
extern crate helix;
use helix::ruby;

ruby! {
class Primitive {
def is_bool(value: ruby::Value) -> bool {
value.is_type(ruby::Type::True) || value.is_type(ruby::Type::False)
}

def as_bool(value: ruby::Value) -> bool {
value.to_rust()
}

def first(ary: ruby::Array) -> ruby::Value {
ary[0]
}
}
}
9 changes: 6 additions & 3 deletions examples/turbo_blank/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ ruby! {

// Delete me:

use helix::{UncheckedValue, ToRust};
use helix::{UncheckedValue, ToRust, ruby};
use helix::coercions::CallFrame;

impl ToString for RubyString {
fn to_string(&self) -> String {
let checked = self.helix.to_checked().unwrap();
checked.to_rust()
let lt = &();
let val = unsafe { ruby::Value::new(self.helix, CallFrame::new(lt)) };
let checked = UncheckedValue::<String>::to_checked(val).unwrap();
ToRust::<String>::to_rust(checked)
}
}
17 changes: 10 additions & 7 deletions src/coercions/bool.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
use sys::{self, VALUE, Qtrue, Qfalse};

use coercions::*;
use ruby::Value;
use super::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby};

impl UncheckedValue<bool> for VALUE {
fn to_checked(self) -> CheckResult<bool> {
if unsafe { sys::RB_TYPE_P(self, sys::T_TRUE) || sys::RB_TYPE_P(self, sys::T_FALSE) } {
Ok(unsafe { CheckedValue::new(self) })
impl<'a> UncheckedValue<bool> for Value<'a> {
type ToRust = CheckedValue<'a, bool>;

fn to_checked(self) -> CheckResult<Self::ToRust> {
if unsafe { sys::RB_TYPE_P(self.inner(), sys::T_TRUE) || sys::RB_TYPE_P(self.inner(), sys::T_FALSE) } {
Ok(unsafe { CheckedValue::<'a, bool>::new(self) })
} else {
Err(format!("No implicit conversion of {} into Rust bool", "?"))
}
}
}

impl ToRust<bool> for CheckedValue<bool> {
impl<'a> ToRust<bool> for CheckedValue<'a, bool> {
fn to_rust(self) -> bool {
self.inner == unsafe { Qtrue }
unsafe { self.inner.inner() == Qtrue }
}
}

Expand Down
Loading