diff options
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | doc/mrbgems/README.md | 54 | ||||
| -rw-r--r-- | doc/mrbgems/c_extension_example/Makefile | 12 | ||||
| -rw-r--r-- | doc/mrbgems/c_extension_example/README.md | 4 | ||||
| -rw-r--r-- | doc/mrbgems/c_extension_example/src/example.c | 17 | ||||
| -rw-r--r-- | doc/mrbgems/c_extension_example/test/example.rb | 3 | ||||
| -rw-r--r-- | doc/mrbgems/ruby_extension_example/Makefile | 7 | ||||
| -rw-r--r-- | doc/mrbgems/ruby_extension_example/README.md | 4 | ||||
| -rw-r--r-- | doc/mrbgems/ruby_extension_example/mrblib/example.rb | 5 | ||||
| -rw-r--r-- | doc/mrbgems/ruby_extension_example/test/example.rb | 3 | ||||
| -rw-r--r-- | mrbgems/Makefile | 63 | ||||
| -rw-r--r-- | mrbgems/Makefile4gem | 37 | ||||
| -rw-r--r-- | mrbgems/g/.gitignore | 1 | ||||
| -rw-r--r-- | mrbgems/generator.c | 305 | ||||
| -rw-r--r-- | mrblib/Makefile | 1 | ||||
| -rw-r--r-- | src/init.c | 2 | ||||
| -rw-r--r-- | test/Makefile | 19 | ||||
| -rw-r--r-- | test/init_mrbtest.c | 3 | ||||
| -rw-r--r-- | tools/mrbc/mrbc.c | 5 |
20 files changed, 542 insertions, 8 deletions
diff --git a/.gitignore b/.gitignore index 797f98917..02ffb598a 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ cscope.out /test/*.*tmp CMakeFiles CMakeCache.txt +/mrbgems/gem_helper +/mrbgems/init_gems.c +/mrbgems/g/Makefile @@ -44,6 +44,7 @@ export CAT := cat all : @$(MAKE) -C src $(MAKE_FLAGS) @$(MAKE) -C mrblib $(MAKE_FLAGS) + @$(MAKE) -C mrbgems $(MAKE_FLAGS) @$(MAKE) -C tools/mruby $(MAKE_FLAGS) @$(MAKE) -C tools/mirb $(MAKE_FLAGS) @@ -56,6 +57,7 @@ test : all .PHONY : clean clean : @$(MAKE) clean -C src $(MAKE_FLAGS) + @$(MAKE) clean -C mrbgems $(MAKE_FLAGS) @$(MAKE) clean -C tools/mruby $(MAKE_FLAGS) @$(MAKE) clean -C tools/mirb $(MAKE_FLAGS) @$(MAKE) clean -C test $(MAKE_FLAGS) diff --git a/doc/mrbgems/README.md b/doc/mrbgems/README.md new file mode 100644 index 000000000..e23ffdcf2 --- /dev/null +++ b/doc/mrbgems/README.md @@ -0,0 +1,54 @@ +# mrbgems + +mrbgems is a library manager to integrate C and Ruby extension in an easy and +standardized way into mruby. + +## GEM Structure + ++- GEM_NAME <- Name of Gem + | + +- mrblib/ <- Source for Ruby extension + | + +- src/ <- Source for C extension + | + +- test/ <- Test code (Ruby) + | + +- Makefile <- Makefile for Gem + | + +- README.md <- Readme for Gem + +## C Extension + +### Example + ++- c_extension_example/ + | + +- src/ + | | + | +- example.c <- C extension source + | + +- test/ + | | + | +- example.rb <- Test code for C extension + | + +- Makefile <- Build rules for C extension + | + +- README.md + +## Ruby Extension + +### Example + ++- ruby_extension_example/ + | + +- mrblib/ + | | + | +- example.rb <- Ruby extension source + | + +- test/ + | | + | +- example.rb <- Test code for Ruby extension + | + +- Makefile + | + +- README.md diff --git a/doc/mrbgems/c_extension_example/Makefile b/doc/mrbgems/c_extension_example/Makefile new file mode 100644 index 000000000..b245e9696 --- /dev/null +++ b/doc/mrbgems/c_extension_example/Makefile @@ -0,0 +1,12 @@ +include ../../Makefile4gem + +GEM := c_extension_example + +GEM_C_FILES := $(wildcard $(SRC_DIR)/*.c) +GEM_OBJECTS := $(patsubst %.c, %.o, $(GEM_C_FILES)) + +gem-all : $(GEM_OBJECTS) + $(AR) rs $(LIBR) $< + +gem-clean : + -$(RM) $(GEM_OBJECTS) diff --git a/doc/mrbgems/c_extension_example/README.md b/doc/mrbgems/c_extension_example/README.md new file mode 100644 index 000000000..3803c2065 --- /dev/null +++ b/doc/mrbgems/c_extension_example/README.md @@ -0,0 +1,4 @@ +C Extension Example +========= + +This is an example gem which implements a C extension. diff --git a/doc/mrbgems/c_extension_example/src/example.c b/doc/mrbgems/c_extension_example/src/example.c new file mode 100644 index 000000000..9f0b07839 --- /dev/null +++ b/doc/mrbgems/c_extension_example/src/example.c @@ -0,0 +1,17 @@ +#include <mruby.h> +#include <stdio.h> + +static struct RClass *_class_cextension; + +static mrb_value +mrb_c_method(mrb_state *mrb, mrb_value self) +{ + puts("A C Extension"); + return self; +} + +void +mrb_c_extension_example_gem_init(mrb_state* mrb) { + _class_cextension = mrb_define_module(mrb, "CExtension"); + mrb_define_class_method(mrb, _class_cextension, "c_method", mrb_c_method, ARGS_NONE()); +} diff --git a/doc/mrbgems/c_extension_example/test/example.rb b/doc/mrbgems/c_extension_example/test/example.rb new file mode 100644 index 000000000..367d18029 --- /dev/null +++ b/doc/mrbgems/c_extension_example/test/example.rb @@ -0,0 +1,3 @@ +assert('C Extension Example') do + CExtension.respond_to? :c_method +end diff --git a/doc/mrbgems/ruby_extension_example/Makefile b/doc/mrbgems/ruby_extension_example/Makefile new file mode 100644 index 000000000..7ebec4f09 --- /dev/null +++ b/doc/mrbgems/ruby_extension_example/Makefile @@ -0,0 +1,7 @@ +include ../../Makefile4gem + +GEM := ruby_extension_example + +gem-all : + +gem-clean : diff --git a/doc/mrbgems/ruby_extension_example/README.md b/doc/mrbgems/ruby_extension_example/README.md new file mode 100644 index 000000000..906a0d8f2 --- /dev/null +++ b/doc/mrbgems/ruby_extension_example/README.md @@ -0,0 +1,4 @@ +Pure Ruby Extension Example +========= + +This is an example gem which implements a pure Ruby extension. diff --git a/doc/mrbgems/ruby_extension_example/mrblib/example.rb b/doc/mrbgems/ruby_extension_example/mrblib/example.rb new file mode 100644 index 000000000..b07a2b580 --- /dev/null +++ b/doc/mrbgems/ruby_extension_example/mrblib/example.rb @@ -0,0 +1,5 @@ +class RubyExtension + def RubyExtension.ruby_method + puts "A Ruby Extension" + end +end diff --git a/doc/mrbgems/ruby_extension_example/test/example.rb b/doc/mrbgems/ruby_extension_example/test/example.rb new file mode 100644 index 000000000..0c1b63469 --- /dev/null +++ b/doc/mrbgems/ruby_extension_example/test/example.rb @@ -0,0 +1,3 @@ +assert('Ruby Extension Example') do + RubyExtension.respond_to? :ruby_method +end diff --git a/mrbgems/Makefile b/mrbgems/Makefile new file mode 100644 index 000000000..a30f804d7 --- /dev/null +++ b/mrbgems/Makefile @@ -0,0 +1,63 @@ +# makefile description. +# add gems to the ruby library + +LIBR := ../lib/libmruby.a +INIT := init_gems +RM_F := rm -f +CC_FLAGS := -Wall -Werror-implicit-function-declaration -g -O3 -MMD -I. -I./../include + +export CC = gcc +export LL = gcc +export AR = ar + +GENERATOR := ./generator +ifeq ($(OS),Windows_NT) + GENERATOR_BIN := $(GENERATOR).exe +else + GENERATOR_BIN := $(GENERATOR) +endif +GEM_MAKEFILE := g/Makefile +GEMDLIB := g/mrbgemtest.ctmp + +############################## +# generic build targets, rules + +.PHONY : all +all : $(INIT).o all_gems + +all_gems : $(GENERATOR_BIN) + @echo "Generate Gem Makefile" + $(GENERATOR_BIN) makefile > $(GEM_MAKEFILE) + @echo "Build all gems" + $(MAKE) -C g + + +$(INIT).c : $(GENERATOR_BIN) + @echo "Generate Gem driver" + $(GENERATOR_BIN) $(INIT) > $@ + +$(INIT).o : $(INIT).c + @echo "Build the driver which initializes all gems" + $(CC) $(CC_FLAGS) -MMD -c $< -o $@ + $(AR) rs $(LIBR) $@ + +# Generator + +$(GENERATOR_BIN) : $(GENERATOR).o + @echo "Build the generator which creates the driver and Gem Makefile" + $(LL) -o $@ $(CC_FLAGS) $< + +$(GENERATOR).o : $(GENERATOR).c + $(CC) $(CC_FLAGS) -MMD -c $< -o $@ + +.PHONY : prepare-test +prepare-test : + @$(MAKE) prepare-test -C g + +# clean driver and all gems +.PHONY : clean +clean : $(GENERATOR_BIN) + @echo "Cleanup Gems" + $(GENERATOR_BIN) makefile > $(GEM_MAKEFILE) + $(MAKE) clean -C g + -$(RM_F) $(INIT).c *.o *.d $(GENERATOR_BIN) $(GEM_MAKEFILE) diff --git a/mrbgems/Makefile4gem b/mrbgems/Makefile4gem new file mode 100644 index 000000000..a4fa3f3c9 --- /dev/null +++ b/mrbgems/Makefile4gem @@ -0,0 +1,37 @@ +# This is the default Makefile integrated +# by each Gem. It integrates important constants +# for usage inside of a Gem. + +# mruby src root +MRUBY_ROOT := ../../../ + +# Tools +CC := gcc +RM := rm -f +AR := ar + +SRC_DIR := src + +INCLUDES := -I$(SRC_DIR) -I$(MRUBY_ROOT)include -I$(MRUBY_ROOT)src -I. +CFLAGS := $(INCLUDES) -O3 -g -Wall -Werror-implicit-function-declaration + +# LIBR can be manipulated with command line arguments +ifeq ($(strip $(LIBR)),) + # default mruby library + LIBR := $(MRUBY_ROOT)lib/libmruby.a +endif + +# Default rules which are calling the +# gem specific gem-all and gem-clean +# implementations of a gem + +.PHONY : all +all : gem-info gem-all + @echo "Gem '$(GEM)' is done" + +gem-info: + @echo "Building Gem '$(GEM)'" + +.PHONY : clean +clean : gem-clean + @echo "Gem '$(GEM)' is clean" diff --git a/mrbgems/g/.gitignore b/mrbgems/g/.gitignore new file mode 100644 index 000000000..f935021a8 --- /dev/null +++ b/mrbgems/g/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/mrbgems/generator.c b/mrbgems/generator.c new file mode 100644 index 000000000..fbcd82c23 --- /dev/null +++ b/mrbgems/generator.c @@ -0,0 +1,305 @@ +#include <string.h> +#include <stdio.h> +#include <dirent.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <mrbconf.h> +#include <stdlib.h> + +static int +one (const struct dirent *unused) +{ + return 1; +} + +/* + * Does a directory exist? + * yes => TRUE + * no => FALSE + * fs error => FALSE + * + */ +static int +directory_exists(char path[4096]) { + DIR* dir = opendir(path); + if (dir) + return TRUE; + else + return FALSE; +} + +/* + * Template generator for each GEM + * + * Arguments: + * before: + * String before each GEM template + * after: + * String after each GEM template + * start: + * String at the start of the template + * end: + * String at the end of the template + * skip_if_src_not_exist: + * TRUE => skip template for GEMs with SRC directory + * FALSE => template for all GEMs + * + */ +static char* +for_each_gem (char before[1024], char after[1024], + char start[1024], char end[1024], + char dir_to_skip[1024]) +{ + struct dirent **eps; + int n; + char gemname[1024] = { 0 }; + char gemname_path[4096] = { 0 }; + char src_path[4096] = { 0 }; + struct stat attribut; + + // return value + char* complete_line = malloc(4096 + sizeof(char)); + strcpy(complete_line, ""); + strcat(complete_line, start); + + n = scandir("./g", &eps, one, alphasort); + if (n >= 0) { + int cnt; + for (cnt = 0; cnt < n; ++cnt) { + strcpy(gemname, eps[cnt]->d_name); + strcpy(gemname_path, "./g/"); + strcat(gemname_path, gemname); + strcpy(src_path, gemname_path); + strcat(src_path, "/src"); + + if (strcmp(gemname, ".") == 0) + continue; + if (strcmp(gemname, "..") == 0) + continue; + if (strcmp(gemname, ".gitignore") == 0) + continue; + + stat(gemname_path, &attribut); + if (S_ISDIR(attribut.st_mode) == 0) { + continue; + } + + if (strcmp(dir_to_skip, "") != 0) { + strcpy(src_path, gemname_path); + strcat(src_path, "/"); + strcat(src_path, dir_to_skip); + + if (directory_exists(src_path) != TRUE) + continue; + } + + strcat(complete_line, before); + strcat(complete_line, gemname); + strcat(complete_line, after); + } + } + else { + perror("Error while scanning the directory."); + } + + strcat(complete_line, end); + return complete_line; +} + +/* + * Gem Makefile Generator + * + */ +void +make_gem_makefile() +{ + char *gem_check = { 0 }; + int gem_empty; + int gem_c_empty; + int gem_ruby_empty; + + printf("CFLAGS := -I. -I../../include -I../../src\n\n" + "ifeq ($(OS),Windows_NT)\n" + "MAKE_FLAGS = --no-print-directory CC=$(CC) LL=$(LL) ALL_CFLAGS='$(ALL_CFLAGS)'\n" + "else\n" + "MAKE_FLAGS = --no-print-directory CC='$(CC)' LL='$(LL)' ALL_CFLAGS='$(ALL_CFLAGS)'\n" + "endif\n\n"); + + /* is there any GEM available? */ + gem_check = for_each_gem("", "", "", "", ""); + if (strcmp(gem_check, "") == 0) + gem_empty = TRUE; + else + gem_empty = FALSE; + + /* is there a C extension available? */ + gem_check = for_each_gem("", "", "", "", "src"); + if (strcmp(gem_check, "") == 0) + gem_c_empty = TRUE; + else + gem_c_empty = FALSE; + + /* is there a Ruby extension available */ + gem_check = for_each_gem("", "", "", "", "mrblib"); + if (strcmp(gem_check, "") == 0) + gem_ruby_empty = TRUE; + else + gem_ruby_empty = FALSE; + + printf(".PHONY : all\n"); + if (gem_empty) { + printf("all :\n\n"); + } + else { + if (gem_c_empty) { + printf("all : mrblib_gem.o\n" + "\t$(AR) rs ../../lib/libmruby.a mrblib_gem.o\n"); + } + else if (gem_ruby_empty) { + printf("all : all_gems\n"); + } + else { + printf("all : all_gems mrblib_gem.o\n" + "\t$(AR) rs ../../lib/libmruby.a mrblib_gem.o\n"); + } + + printf("\n"); + + // Rule for building all C extensions of each Gem + if (!gem_c_empty) { + printf("all_gems :\n%s\n", + for_each_gem("\t@$(MAKE) -C ", " $(MAKE_FLAGS)\n", "", "", "") + ); + } + + // Rule for building all Ruby Extension of each Gem + if (!gem_ruby_empty) { + printf("mrblib_gem.o : mrblib_gem.c\n\n" + + "mrblib_gem.c : mrblib_gem.ctmp\n" + "\tcat $< > $@\n\n" + + "mrblib_gem.ctmp : mrblib_gem.rbtmp\n" + "\t../../bin/mrbc -Bmrblib_gem_irep -o$@ $<\n\n" + + "mrblib_gem.rbtmp :\n%s\n", + for_each_gem(" ", "/mrblib/*.rb", "\tcat", "> mrblib_gem.rbtmp", "mrblib") + ); + } + } + + printf("\n.PHONY : prepare-test\n" + "prepare-test :\n" + ); + if (!gem_empty) + printf("%s", + for_each_gem(" ", "/test/*.rb ", "\tcat", " > mrbgemtest.rbtmp", "") + ); + else + printf("\t../generator rbtmp > mrbgemtest.rbtmp"); + printf("\n\t../../bin/mrbc -Bmrbgemtest_irep -omrbgemtest.ctmp mrbgemtest.rbtmp\n\n"); + + printf(".PHONY : clean\n" + "clean :\n" + "\t$(RM) *.c *.d *.rbtmp *.ctmp *.o mrbtest\n"); + if (!gem_empty) + printf("%s", + for_each_gem("\t@$(MAKE) clean -C ", " $(MAKE_FLAGS)\n", "", "", "") + ); +} + +/* + * init_gems.c Generator + * + */ +void +make_init_gems() +{ + char *gem_check = { 0 }; + int gem_c_empty; + int gem_ruby_empty; + + /* is there a C extension available? */ + gem_check = for_each_gem("", "", "", "", "src"); + if (strcmp(gem_check, "") == 0) + gem_c_empty = TRUE; + else + gem_c_empty = FALSE; + + /* is there a Ruby extension available */ + gem_check = for_each_gem("", "", "", "", "mrblib"); + if (strcmp(gem_check, "") == 0) + gem_ruby_empty = TRUE; + else + gem_ruby_empty = FALSE; + + printf("/*\n" + " * This file contains a list of all\n" + " * initializing methods which are\n" + " * necessary to bootstrap all gems.\n" + " *\n" + " * IMPORTANT:\n" + " * This file was generated!\n" + " * All manual changes will get lost.\n" + " */\n\n" + "#include \"mruby.h\"\n" + "#include \"mruby/irep.h\"\n" + "#include \"mruby/dump.h\"\n" + "#include \"mruby/string.h\"\n" + "#include \"mruby/proc.h\"\n"); + + if (!gem_c_empty) + printf("\n%s", + for_each_gem("void mrb_", "_gem_init(mrb_state*);\n", "", "", "src") + ); + + if (!gem_ruby_empty) + printf("\nextern const char mrblib_gem_irep[];\n"); + + printf("\nvoid\n" + "mrb_init_mrbgems(mrb_state *mrb) {\n"); + + if (!gem_c_empty) + printf("%s", + for_each_gem(" mrb_", "_gem_init(mrb);\n", "", "", "src") + ); + + if (!gem_ruby_empty) { + printf(" int n = mrb_read_irep(mrb, mrblib_gem_irep);\n" + " mrb_run(mrb, mrb_proc_new(mrb, mrb->irep[n]), mrb_top_self(mrb));\n" + " if (mrb->exc) {\n" + " mrb_p(mrb, mrb_obj_value(mrb->exc));\n" + " exit(0);\n" + " }\n"); + } + + printf("}"); +} + +void +make_rbtmp() +{ + printf("\n"); +} + +int +main (int argc, char *argv[]) +{ + if (argc == 2) { + if (strcmp(argv[1], "makefile") == 0) + make_gem_makefile(); + else if (strcmp(argv[1], "init_gems") == 0) + make_init_gems(); + else if (strcmp(argv[1], "rbtmp") == 0) + make_rbtmp(); + else + return 1; + } + else { + printf("Argument missing! Options: 'makefile', 'init_gems', 'rbtmp'"); + return 1; + } + + return 0; +} diff --git a/mrblib/Makefile b/mrblib/Makefile index 6d7ac65f9..942b3e1d2 100644 --- a/mrblib/Makefile +++ b/mrblib/Makefile @@ -52,7 +52,6 @@ endif .PHONY : all all : $(LIBR) -# update libmruby.a $(LIBR) : $(MLIB) $(LIBR0) $(CP) $(LIBR0) $(LIBR) $(AR) r $(LIBR) $(MLIB) diff --git a/src/init.c b/src/init.c index 52fd9e118..1a7e72686 100644 --- a/src/init.c +++ b/src/init.c @@ -27,6 +27,7 @@ void mrb_init_print(mrb_state*); void mrb_init_time(mrb_state*); void mrb_init_math(mrb_state*); void mrb_init_mrblib(mrb_state*); +void mrb_init_mrbgems(mrb_state*); void @@ -66,6 +67,7 @@ mrb_init_core(mrb_state *mrb) #endif mrb_init_mrblib(mrb); + mrb_init_mrbgems(mrb); mrb_gc_arena_restore(mrb, 0); } diff --git a/test/Makefile b/test/Makefile index 18bc79b5a..183e03438 100644 --- a/test/Makefile +++ b/test/Makefile @@ -10,6 +10,8 @@ MLIB := $(TARGET).o CLIB := $(TARGET).c INIT := init_$(TARGET).c DLIB := $(TARGET).ctmp +GEMDIR := ../mrbgems +GEMDLIB := $(GEMDIR)/g/mrbgemtest.ctmp RLIB := $(TARGET).rbtmp DEPLIB := $(TARGET).d driver.d ASSLIB := $(BASEDIR)/assert.rb @@ -42,14 +44,13 @@ endif # mruby compiler and test driver ifeq ($(OS),Windows_NT) -MRBC = ../bin/mrbc.exe -EXE := $(TARGET).exe + MRBC = ../bin/mrbc.exe + EXE := $(TARGET).exe else -MRBC = ../bin/mrbc -EXE := $(TARGET) + MRBC = ../bin/mrbc + EXE := $(TARGET) endif - ############################## # generic build targets, rules @@ -68,8 +69,12 @@ $(OBJS) : %.o : %.c $(CC) $(ALL_CFLAGS) -MMD $(INCLUDES) -c $< -o $@ # Compile C source from merged mruby source -$(CLIB) : $(RLIB) $(MRBC) $(INIT) - $(MRBC) -Bmrbtest_irep -o$(DLIB) $(RLIB); $(CAT) $(INIT) $(DLIB) > $@ +$(CLIB) : $(DLIB) $(INIT) + @$(MAKE) prepare-test -C $(GEMDIR) + $(CAT) $(INIT) $(DLIB) $(GEMDLIB) > $@ + +$(DLIB) : $(RLIB) $(MRBC) + $(MRBC) -Bmrbtest_irep -o$@ $(RLIB) # merge mruby sources $(RLIB) : $(ASSLIB) $(MRBS) diff --git a/test/init_mrbtest.c b/test/init_mrbtest.c index b9f09dd2f..2bc2f2e4b 100644 --- a/test/init_mrbtest.c +++ b/test/init_mrbtest.c @@ -5,13 +5,16 @@ #include "mruby/proc.h" extern const char mrbtest_irep[]; +extern const char mrbgemtest_irep[]; void mrb_init_mrbtest(mrb_state *mrb) { int n = mrb_read_irep(mrb, mrbtest_irep); + int m = mrb_read_irep(mrb, mrbgemtest_irep); mrb_run(mrb, mrb_proc_new(mrb, mrb->irep[n]), mrb_top_self(mrb)); + mrb_run(mrb, mrb_proc_new(mrb, mrb->irep[m]), mrb_top_self(mrb)); if (mrb->exc) { mrb_p(mrb, mrb_obj_value(mrb->exc)); exit(0); diff --git a/tools/mrbc/mrbc.c b/tools/mrbc/mrbc.c index 5382b90a8..62bc34700 100644 --- a/tools/mrbc/mrbc.c +++ b/tools/mrbc/mrbc.c @@ -212,3 +212,8 @@ void mrb_init_mrblib(mrb_state *mrb) { } + +void +mrb_init_mrbgems(mrb_state *mrb) +{ +} |
