diff options
28 files changed, 982 insertions, 13 deletions
diff --git a/.gitignore b/.gitignore index 8a76394ee..420533b61 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,9 @@ cscope.out /test/mrubytest.* CMakeFiles CMakeCache.txt +/mrbgems/generator +/mrbgems/gem_init.c +/mrbgems/g/Makefile +/mrbgems/g/MakefileGemList +/mrbgems/g/mrbgemtest.ctmp +/mrbgems/g/mrbgemtest.rbtmp @@ -7,6 +7,18 @@ export LL = gcc export AR = ar export YACC = bison +MRUBY_ROOT := $(realpath .) + +ifeq ($(strip $(ENABLE_GEMS)),) + # by default GEMs are deactivated + ENABLE_GEMS = false +endif + +ifeq ($(strip $(ACTIVE_GEMS)),) + # the default file which contains the active GEMs + ACTIVE_GEMS = GEMS.active +endif + ifeq ($(strip $(COMPILE_MODE)),) # default compile option COMPILE_MODE = debug @@ -22,9 +34,9 @@ endif ALL_CFLAGS = -Wall -Werror-implicit-function-declaration $(CFLAGS) ifeq ($(OS),Windows_NT) - MAKE_FLAGS = --no-print-directory CC=$(CC) LL=$(LL) ALL_CFLAGS='$(ALL_CFLAGS)' + MAKE_FLAGS = --no-print-directory CC=$(CC) LL=$(LL) ALL_CFLAGS='$(ALL_CFLAGS)' ENABLE_GEMS='$(ENABLE_GEMS)' ACTIVE_GEMS='$(ACTIVE_GEMS)' MRUBY_ROOT='$(MRUBY_ROOT)' else - MAKE_FLAGS = --no-print-directory CC='$(CC)' LL='$(LL)' ALL_CFLAGS='$(ALL_CFLAGS)' + MAKE_FLAGS = --no-print-directory CC='$(CC)' LL='$(LL)' ALL_CFLAGS='$(ALL_CFLAGS)' ENABLE_GEMS='$(ENABLE_GEMS)' ACTIVE_GEMS='$(ACTIVE_GEMS)' MRUBY_ROOT='$(MRUBY_ROOT)' endif ############################## @@ -44,7 +56,11 @@ export CAT := cat all : @$(MAKE) -C src $(MAKE_FLAGS) @$(MAKE) -C mrblib $(MAKE_FLAGS) - @$(MAKE) -C tools/mruby $(MAKE_FLAGS) +ifeq ($(ENABLE_GEMS),true) + @echo "-- MAKE mrbgems --" + @$(MAKE) -C mrbgems $(MAKE_FLAGS) +endif + $(MAKE) -C tools/mruby $(MAKE_FLAGS) @$(MAKE) -C tools/mirb $(MAKE_FLAGS) # mruby test @@ -56,6 +72,10 @@ test : all .PHONY : clean clean : @$(MAKE) clean -C src $(MAKE_FLAGS) +ifeq ($(ENABLE_GEMS),true) + @echo "-- CLEAN mrbgems --" + @$(MAKE) clean -C mrbgems $(MAKE_FLAGS) +endif @$(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..98ff44720 --- /dev/null +++ b/doc/mrbgems/README.md @@ -0,0 +1,179 @@ +# mrbgems + +mrbgems is a library manager to integrate C and Ruby extension in an easy and +standardised way into mruby. + +## Usage + +By default mrbgems is currently deactivated. As long as mrbgems is deactivated +there is no overhead inside of the mruby interpreter. + +To activate you have to make the following changes: +* set *ENABLE_GEMS* to *true* in *$(MRUBY_ROOT)/Makefile* +* define *ENABLE_GEMS* in *$(MRUBY_ROOT)/include/mrbconf.h* +* activate GEMs in *$(MRUBY_ROOT)/mrbgems/GEMS.active* + +Every activated GEM has to be listed with his absolute path in *GEMS.active*. It +is possible to point to an alternative activate file: +* set *ACTIVE_GEMS* to your customized GEM list in *$(MRUBY_ROOT)/Makefile* + +## GEM Structure + +The maximal GEM structure looks like this: + +``` ++- 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 +``` + +The folder *mrblib* contains pure Ruby files to extend mruby. The folder *src* +contains C files to extend mruby. The folder *test* contains pure Ruby files +for testing purposes which will be used by *mrbtest*. The *Makefile* contains +rules to build a *gem.a* file inside of the GEM directory. Which will be used +for integration into the normal mruby build process. *README.md* is a short +description of your GEM. + +## Build process + +mrbgems will call *make* to build and *make clean* to clean your GEM. You +have to build a *mrb-GEMNAME-gem.a* file during this build process. How you +are going to do this is up to you. + +To make your build process more easier and more standardized we suggest +to include *mrbgems/Makefile4gem* which defines some helper rules. In +case you include this Makefile you have to define specific pre-defined +rules like *gem-all* for the build process and *gem-clean* for the clean +process. There are additional helper rules for specific GEM examples +below. + +## C Extension + +mruby can be extended with C. It is possible by using the C API to +integrate C libraries into mruby. + +The *Makefile* is used for building a C extension. You should +define *GEM* (GEM name), *GEM_C_FILES* (all C files) and +*GEM_OBJECTS* (all Object files). Pay also attention that your +*Makefile* has to build the object files. You can use +*gem-c-files* to build a *mrb-GEMNAME-gem.a* out of your +Object code and use *gem-clean-c-files* to clean the object files. + +### Pre-Conditions + +mrbgems expects that you have implemented a C method called +*mrb_YOURGEMNAME_gem_init(mrb_state)*. YOURGEMNAME will be replaced +by the name of you GEM. The directory name of your GEM is considered also +as the name! If you call your GEM directory *c_extension_example*, your +initialisation method could look like this: + +``` +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()); +} +``` + +mrbgems will also use the *gem-clean* make target to clean up your GEM. Implement +this target with the necessary rules! + +### 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 + +mruby can be extended with pure Ruby. It is possible to override existing +classes or add new ones in this way. Put all Ruby files into the *mrblib* +folder. + +The *Makefile* is used for building a Ruby extension. You should define +*GEM* (GEM name) and *GEM_RB_FILES* (all Ruby files). You can use +*gem-rb-files* to build a *mrb-GEMNAME-gem.a* out of your Ruby code and use +*gem-clean-rb-files* to clean the generated C files. + +### Pre-Conditions + +mrbgems will automatically call the *gem-all* make target of your GEM. + +mrbgems will also use the *gem-clean* make target to clean up your GEM. Implement +this target with the necessary rules! + +### Example + +``` ++- ruby_extension_example/ + | + +- mrblib/ + | | + | +- example.rb <- Ruby extension source + | + +- test/ + | | + | +- example.rb <- Test code for Ruby extension + | + +- Makefile + | + +- README.md +``` + +## C and Ruby Extension + +mruby can be extended with C and Ruby at the same time. It is possible to +override existing classes or add new ones in this way. Put all Ruby files +into the *mrblib* folder and all C files into the *src* folder. + +The *Makefile* is used for building a C and Ruby extension. You should +define *GEM* (GEM name), *GEM_C_FILES* (all C files), *GEM_OBJECTS* +(all Object files) and *GEM_RB_FILES* (all Ruby files). You can use +*gem-c-and-rb-files* to build a *mrb-GEMNAME-gem.a* out of your Object +and Ruby code. Use *gem-clean-c-and-rb-files* to clean the generated +C files. + +### Pre-Conditions + +See C and Ruby example. + +### Example + +``` ++- c_and_ruby_extension_example/ + | + +- mrblib/ + | | + | +- example.rb <- Ruby extension source + | + +- src/ + | | + | +- example.c <- C extension source + | + +- test/ + | | + | +- example.rb <- Test code for C and Ruby extension + | + +- Makefile + | + +- README.md diff --git a/doc/mrbgems/c_and_ruby_extension_example/Makefile b/doc/mrbgems/c_and_ruby_extension_example/Makefile new file mode 100644 index 000000000..57912a936 --- /dev/null +++ b/doc/mrbgems/c_and_ruby_extension_example/Makefile @@ -0,0 +1,12 @@ +GEM := c_and_ruby_extension_example + +include $(MAKEFILE_4_GEM) + +GEM_C_FILES := $(wildcard $(SRC_DIR)/*.c) +GEM_OBJECTS := $(patsubst %.c, %.o, $(GEM_C_FILES)) + +GEM_RB_FILES := $(wildcard $(MRB_DIR)/*.rb) + +gem-all : $(GEM_OBJECTS) gem-c-and-rb-files + +gem-clean : gem-clean-c-and-rb-files diff --git a/doc/mrbgems/c_and_ruby_extension_example/README.md b/doc/mrbgems/c_and_ruby_extension_example/README.md new file mode 100644 index 000000000..0b428b0b6 --- /dev/null +++ b/doc/mrbgems/c_and_ruby_extension_example/README.md @@ -0,0 +1,4 @@ +C and Ruby Extension Example +========= + +This is an example gem which implements a C and Ruby extension. diff --git a/doc/mrbgems/c_and_ruby_extension_example/mrblib/example.rb b/doc/mrbgems/c_and_ruby_extension_example/mrblib/example.rb new file mode 100644 index 000000000..0c2d3c7d1 --- /dev/null +++ b/doc/mrbgems/c_and_ruby_extension_example/mrblib/example.rb @@ -0,0 +1,5 @@ +class RubyExtension + def CRubyExtension.ruby_method + puts "A Ruby Extension" + end +end diff --git a/doc/mrbgems/c_and_ruby_extension_example/src/example.c b/doc/mrbgems/c_and_ruby_extension_example/src/example.c new file mode 100644 index 000000000..178514ffe --- /dev/null +++ b/doc/mrbgems/c_and_ruby_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_and_ruby_extension_example_gem_init(mrb_state* mrb) { + _class_cextension = mrb_define_module(mrb, "CRubyExtension"); + mrb_define_class_method(mrb, _class_cextension, "c_method", mrb_c_method, ARGS_NONE()); +} diff --git a/doc/mrbgems/c_and_ruby_extension_example/test/example.rb b/doc/mrbgems/c_and_ruby_extension_example/test/example.rb new file mode 100644 index 000000000..fffad710f --- /dev/null +++ b/doc/mrbgems/c_and_ruby_extension_example/test/example.rb @@ -0,0 +1,7 @@ +assert('C and Ruby Extension Example 1') do + CRubyExtension.respond_to? :c_method +end + +assert('C and Ruby Extension Example 2') do + CRubyExtension.respond_to? :ruby_method +end diff --git a/doc/mrbgems/c_extension_example/Makefile b/doc/mrbgems/c_extension_example/Makefile new file mode 100644 index 000000000..339669970 --- /dev/null +++ b/doc/mrbgems/c_extension_example/Makefile @@ -0,0 +1,10 @@ +GEM := c_extension_example + +include $(MAKEFILE_4_GEM) + +GEM_C_FILES := $(wildcard $(SRC_DIR)/*.c) +GEM_OBJECTS := $(patsubst %.c, %.o, $(GEM_C_FILES)) + +gem-all : $(GEM_OBJECTS) gem-c-files + +gem-clean : gem-clean-c-files 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..9c5026744 --- /dev/null +++ b/doc/mrbgems/ruby_extension_example/Makefile @@ -0,0 +1,9 @@ +GEM := ruby_extension_example + +include $(MAKEFILE_4_GEM) + +GEM_RB_FILES := $(wildcard $(MRB_DIR)/*.rb) + +gem-all : gem-rb-files + +gem-clean : gem-clean-rb-files 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/include/mrbconf.h b/include/mrbconf.h index 20c9a59a8..42ff4b80c 100644 --- a/include/mrbconf.h +++ b/include/mrbconf.h @@ -50,6 +50,7 @@ //#define DISABLE_TIME /* Time class */ //#define DISABLE_STRUCT /* Struct class */ //#define DISABLE_STDIO /* use of stdio */ +#define DISABLE_GEMS /* Package Manager mrbgems */ #undef HAVE_UNISTD_H /* WINDOWS */ #define HAVE_UNISTD_H /* LINUX */ diff --git a/mrbgems/GEMS.active b/mrbgems/GEMS.active new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/mrbgems/GEMS.active diff --git a/mrbgems/Makefile b/mrbgems/Makefile new file mode 100644 index 000000000..c7d1f577d --- /dev/null +++ b/mrbgems/Makefile @@ -0,0 +1,76 @@ +# makefile description. +# add gems to the ruby library + +ifeq ($(strip $(MRUBY_ROOT)),) + MRUBY_ROOT := $(realpath ..) +endif + +LIBR := ../lib/libmruby.a +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 := $(MRUBY_ROOT)/mrbgems/generator +ifeq ($(OS),Windows_NT) + GENERATOR_BIN := $(GENERATOR).exe +else + GENERATOR_BIN := $(GENERATOR) +endif +GEM_INIT := gem_init +GEM_MAKEFILE := g/Makefile +GEM_MAKEFILE_LIST := g/MakefileGemList +GEMDLIB := g/mrbgemtest.ctmp + +ifeq ($(strip $(ACTIVE_GEMS)),) + # the default file which contains the active GEMs + ACTIVE_GEMS = GEMS.active +endif + +############################## +# generic build targets, rules + +.PHONY : all +all : all_gems $(GEM_INIT).a + +$(GEM_INIT).a : $(GEM_INIT).o + $(AR) rs gem_init.a $(GEM_INIT).o + +all_gems : $(GENERATOR_BIN) + @echo "Generate Gem List Makefile" + $(GENERATOR_BIN) makefile_list "$(ACTIVE_GEMS)" > $(GEM_MAKEFILE_LIST) + @echo "Generate Gem Makefile" + $(GENERATOR_BIN) makefile "$(ACTIVE_GEMS)" > $(GEM_MAKEFILE) + @echo "Build all gems" + $(MAKE) -C g MRUBY_ROOT='$(MRUBY_ROOT)' ACTIVE_GEMS="$(ACTIVE_GEMS)" + +$(GEM_INIT).c : $(GENERATOR_BIN) + @echo "Generate Gem driver" + $(GENERATOR_BIN) $(GEM_INIT) "$(ACTIVE_GEMS)" > $@ + +$(GEM_INIT).o : $(GEM_INIT).c + @echo "Build the driver which initializes all gems" + $(CC) $(CC_FLAGS) -MMD -c $< -o $@ + +# 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 ACTIVE_GEMS="$(ACTIVE_GEMS)" MRUBY_ROOT='$(MRUBY_ROOT)' + +# clean driver and all gems +.PHONY : clean +clean : $(GENERATOR_BIN) + @echo "Cleanup Gems" + $(GENERATOR_BIN) makefile "$(ACTIVE_GEMS)" > $(GEM_MAKEFILE) + $(MAKE) clean -C g ACTIVE_GEMS="$(ACTIVE_GEMS)" MRUBY_ROOT='$(MRUBY_ROOT)' + -$(RM_F) $(GEM_INIT).c *.o *.d $(GENERATOR_BIN) $(GEM_MAKEFILE) $(GEM_MAKEFILE_LIST) gem_init.a diff --git a/mrbgems/Makefile4gem b/mrbgems/Makefile4gem new file mode 100644 index 000000000..763210659 --- /dev/null +++ b/mrbgems/Makefile4gem @@ -0,0 +1,91 @@ +# This is the default Makefile integrated +# by each Gem. It integrates important constants +# for usage inside of a Gem. + +ifeq ($(strip $(MRUBY_ROOT)),) + # mruby src root + MRUBY_ROOT := $(realpath ../../..) +endif + +# Tools +CC := gcc +RM := rm -f +AR := ar + +SRC_DIR := src +MRB_DIR := mrblib + +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 + +GEM_PACKAGE := mrb-$(GEM)-gem.a + +ifeq ($(strip $(ACTIVE_GEMS)),) + # the default file which contains the active GEMs + ACTIVE_GEMS = GEMS.active +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 + +gem-info: + @echo "Building Gem '$(GEM)'" + +# Building target for C and Ruby files +gem-c-and-rb-files : gem_mixlib.o + $(AR) rs $(GEM_PACKAGE) $(GEM_OBJECTS) $^ + +gem_mixlib.c : gem_mrblib_header.ctmp gem_mrblib_irep.ctmp gem_mixlib_init.ctmp + cat $^ > $@ + +gem_mixlib_init.ctmp : + $(MRUBY_ROOT)/mrbgems/generator gem_mixlib $(GEM) "$(ACTIVE_GEMS)" > $@ + +# Building target for C files +gem-c-files : gem_srclib.o + $(AR) rs $(GEM_PACKAGE) $(GEM_OBJECTS) $< + +gem_srclib.c : + $(MRUBY_ROOT)/mrbgems/generator gem_srclib $(GEM) "$(ACTIVE_GEMS)" > $@ + +# Building target for Ruby Files +gem-rb-files : gem_mrblib.o + $(AR) rs $(GEM_PACKAGE) $< + +gem_mrblib.c : gem_mrblib_header.ctmp gem_mrblib_irep.ctmp gem_mrblib_init.ctmp + cat $^ > $@ + +gem_mrblib_header.ctmp : + $(MRUBY_ROOT)/mrbgems/generator gem_mrblib "$(ACTIVE_GEMS)" > $@ + +gem_mrblib_init.ctmp : + $(MRUBY_ROOT)/mrbgems/generator gem_mrblib $(GEM) "$(ACTIVE_GEMS)" > $@ + +gem_mrblib_irep.ctmp : gem_mrblib.rbtmp + $(MRUBY_ROOT)/bin/mrbc -Bgem_mrblib_irep_$(GEM) -o$@ $< + +gem_mrblib.rbtmp : + cat $(GEM_RB_FILES) > $@ + +gem-clean-c-and-rb-files : + -$(RM) $(GEM_PACKAGE) gem_mixlib.o gem_mixlib.c gem_mrblib_header.ctmp gem_mrblib_irep.ctmp gem_mixlib_init.ctmp gem_mrblib.rbtmp + +gem-clean-c-files : + -$(RM) $(GEM_PACKAGE) gem_srclib.c gem_srclib.o $(GEM_OBJECTS) + +gem-clean-rb-files : + -$(RM) $(GEM_PACKAGE) gem_mrblib.o gem_mrblib.c gem_mrblib_header.ctmp gem_mrblib_init.ctmp gem_mrblib_irep.ctmp gem_mrblib.rbtmp + +.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..dcc9b5e34 --- /dev/null +++ b/mrbgems/generator.c @@ -0,0 +1,420 @@ +/* +** generator.c - Generator for mrbgems +** +** See Copyright Notice in mruby.h +*/ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <mrbconf.h> + +/* + * Get the file name part of *path* + * + * Arguments: + * path: + * String which represents the path + * + */ +static char +*get_file_name(char *path) +{ + char *base = strrchr(path, '/'); + return base ? base+1 : path; +} + +/* + * Search in *s* for *old* and replace with *new*. + * + * Arguments: + * s: + * String in which the the replacement will be done + * old: + * String which will be replaced + * new: + * String which will be the replacement + * + */ +static char +*replace(const char *s, const char *old, const char *new) +{ + char *ret; + int i, count = 0; + size_t newlen = strlen(new); + size_t oldlen = strlen(old); + + for (i = 0; s[i] != '\0'; i++) { + if (strstr(&s[i], old) == &s[i]) { + count++; + i += oldlen - 1; + } + } + + ret = malloc(i + count * (newlen - oldlen)); + if (ret == NULL) + exit(EXIT_FAILURE); + + i = 0; + while (*s) { + if (strstr(s, old) == s) { + strcpy(&ret[i], new); + i += newlen; + s += oldlen; + } + else + ret[i++] = *s++; + } + ret[i] = '\0'; + + return ret; +} + +/* + * Template generator for each active 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 + * full_path + * Should the full path of the GEM be used? + * + */ +static char* +for_each_gem (char before[1024], char after[1024], + char start[1024], char end[1024], + int full_path, char active_gems[1024]) +{ + /* active GEM check */ + FILE *active_gem_file; + char gem_char; + char gem_name[1024] = { 0 }; + int char_index; + char gem_list[1024][1024] = { { 0 }, { 0 } }; + int gem_index; + int i; + + /* return value */ + char* complete_line = malloc(4096 + sizeof(char)); + strcpy(complete_line, ""); + strcat(complete_line, start); + + /* Read out the active GEMs */ + active_gem_file = fopen(active_gems, "r+"); + if (active_gem_file != NULL) { + char_index = 0; + gem_index = 0; + while((gem_char = fgetc(active_gem_file)) != EOF) { + if (gem_char == '\n') { + /* Every line contains one active GEM */ + gem_name[char_index++] = '\0'; + strcpy(gem_list[gem_index++], gem_name); + + gem_name[0] = '\0'; + char_index = 0; + } + else + gem_name[char_index++] = gem_char; + } + + fclose(active_gem_file); + } + else { /* Error: Active GEM list couldn't be loaded */ } + + for(i = 0; i < gem_index; i++) { + strcat(complete_line, before); + if (full_path == TRUE) + strcat(complete_line, gem_list[i]); + else + strcat(complete_line, get_file_name(gem_list[i])); + strcat(complete_line, replace(after, "#GEMNAME#", get_file_name(gem_list[i]))); + } + + strcat(complete_line, end); + return complete_line; +} + +/* + * Gem Makefile Generator + * + * Global Makefile which starts the build process + * for every active GEM. + * + */ +static void +make_gem_makefile(char active_gems[1024]) +{ + char *gem_check = { 0 }; + int gem_empty; + + printf("ifeq ($(strip $(MRUBY_ROOT)),)\n" + " MRUBY_ROOT := $(realpath ../../..)\n" + "endif\n\n" + "MAKEFILE_4_GEM := $(MRUBY_ROOT)/mrbgems/Makefile4gem\n\n" + "CFLAGS := -I. -I$(MRUBY_ROOT)/include -I$(MRUBY_ROOT)/src\n\n" + "ifeq ($(OS),Windows_NT)\n" + " MAKE_FLAGS = --no-print-directory CC=$(CC) LL=$(LL) ALL_CFLAGS='$(ALL_CFLAGS)' MRUBY_ROOT='$(MRUBY_ROOT)' MAKEFILE_4_GEM='$(MAKEFILE_4_GEM)'\n" + "else\n" + " MAKE_FLAGS = --no-print-directory CC='$(CC)' LL='$(LL)' ALL_CFLAGS='$(ALL_CFLAGS)' MRUBY_ROOT='$(MRUBY_ROOT)' MAKEFILE_4_GEM='$(MAKEFILE_4_GEM)'\n" + "endif\n\n"); + + /* is there any GEM available? */ + gem_check = for_each_gem("", "", "", "", TRUE, active_gems); + if (strcmp(gem_check, "") == 0) + gem_empty = TRUE; + else + gem_empty = FALSE; + + /* Makefile Rules to build every single GEM */ + + printf(".PHONY : all\n"); + if (gem_empty) + printf("all :\n\n"); + else { + printf("all : all_gems\n\n"); + + /* Call make for every GEM */ + printf("all_gems :\n%s\n", + for_each_gem("\t@$(MAKE) -C ", " $(MAKE_FLAGS)\n", "", "", TRUE, active_gems) + ); + printf("\n"); + } + + /* Makefile Rules to Test GEMs */ + + printf(".PHONY : prepare-test\n" + "prepare-test :\n" + ); + if (!gem_empty) + printf("%s", + for_each_gem(" ", "/test/*.rb ", "\tcat", " > mrbgemtest.rbtmp", TRUE, active_gems) + ); + else + printf("\t$(MRUBY_ROOT)/mrbgems/generator rbtmp \"%s\"> mrbgemtest.rbtmp", active_gems); + + printf("\n\t$(MRUBY_ROOT)/bin/mrbc -Bmrbgemtest_irep -omrbgemtest.ctmp mrbgemtest.rbtmp\n\n"); + + /* Makefile Rules to Clean GEMs */ + + 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", "", "", TRUE, active_gems) + ); +} + +/* + * Gem Makefile List Generator + * + * Creates a Makefile which will be included by other Makefiles + * which need to know which GEMs are active. + * + */ +static void +make_gem_makefile_list(char active_gems[1024]) +{ + printf("%s", + for_each_gem(" ", "/mrb-#GEMNAME#-gem.a", "GEM_LIST := ", "\n", TRUE, active_gems) + ); + + printf("GEM_ARCHIVE_FILES := $(MRUBY_ROOT)/mrbgems/gem_init.a\n" + "GEM_ARCHIVE_FILES += $(GEM_LIST)\n\n"); +} + +/* + * gem_init.c Generator + * + */ +static void +make_gem_init(char active_gems[1024]) +{ + 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"); + + /* Protoype definition of all initialization methods */ + printf("\n%s", + for_each_gem("void GENERATED_TMP_mrb_", "_gem_init(mrb_state*);\n", "", "", FALSE, active_gems) + ); + printf("\n"); + + /* mrb_init_mrbgems(mrb) method for initialization of all GEMs */ + printf("void\n" + "mrb_init_mrbgems(mrb_state *mrb) {\n"); + printf( "%s", + for_each_gem(" GENERATED_TMP_mrb_", "_gem_init(mrb);\n", "", "", FALSE, active_gems) + ); + printf("}"); +} + +/* + * Empty Generator + * + * Generates a clean file with one new line. + * + */ +static void +make_rbtmp(char active_gems[1024]) +{ + printf("\n"); +} + +/* + * Header Generator + * + * Head of the C Code for loading the GEMs into the interpreter. + * + */ +static void +make_gem_mrblib_header(char active_gems[1024]) +{ + printf("/*\n" + " * This file is loading the irep\n" + " * Ruby GEM code.\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\n"); +} + +/* + * mrblib Generator + * + * Generates the C Code for loading + * the pure Ruby GEMs into the interpreter. + * + */ +static void +make_gem_mrblib(char argv[1024], char active_gems[1024]) +{ + printf("\n" + "void\n" + "GENERATED_TMP_mrb_%s_gem_init(mrb_state *mrb) {\n" + " mrb_load_irep(mrb, gem_mrblib_irep_%s);\n" + " if (mrb->exc) {\n" + " mrb_p(mrb, mrb_obj_value(mrb->exc));\n" + " exit(0);\n" + " }\n" + "}", argv, argv); +} + +/* + * srclib Generator + * + * Generates the C Code for loading + * the pure C GEMs into the interpreter. + * + */ +static void +make_gem_srclib(char argv[1024], char active_gems[1024]) +{ + printf("/*\n" + " * This file is loading the irep\n" + " * Ruby GEM code.\n" + " *\n" + " * IMPORTANT:\n" + " * This file was generated!\n" + " * All manual changes will get lost.\n" + " */\n\n" + "#include \"mruby.h\"\n"); + + printf("\n" + "void mrb_%s_gem_init(mrb_state*);\n", argv); + + printf("\n" + "void\n" + "GENERATED_TMP_mrb_%s_gem_init(mrb_state *mrb) {\n" + " mrb_%s_gem_init(mrb);\n" + "}", argv, argv); +} + +/* + * mixlib Generator + * + * Generates the C Code for loading + * the mixed Ruby and C GEMs + * into the interpreter. + * + */ +static void +make_gem_mixlib(char argv[1024], char active_gems[1024]) +{ + printf("\n" + "void mrb_%s_gem_init(mrb_state*);\n", argv); + + printf("\n" + "void\n" + "GENERATED_TMP_mrb_%s_gem_init(mrb_state *mrb) {\n" + " mrb_%s_gem_init(mrb);\n" + " mrb_load_irep(mrb, gem_mrblib_irep_%s);\n" + " if (mrb->exc) {\n" + " mrb_p(mrb, mrb_obj_value(mrb->exc));\n" + " exit(0);\n" + " }\n" + "}", argv, argv, argv); +} + +/* + * Start the generator and decide what to generate. + * + */ +int +main (int argc, char *argv[]) +{ + const char * argument_info = "Wrong argument! Options: 'makefile', 'gem_init', 'rbtmp', 'gem_mrblib', gem_srclib\n"; + if (argc == 3) { + if (strcmp(argv[1], "makefile") == 0) + make_gem_makefile(argv[2]); + else if (strcmp(argv[1], "makefile_list") == 0) + make_gem_makefile_list(argv[2]); + else if (strcmp(argv[1], "gem_init") == 0) + make_gem_init(argv[2]); + else if (strcmp(argv[1], "rbtmp") == 0) + make_rbtmp(argv[2]); + else if (strcmp(argv[1], "gem_mrblib") == 0) + make_gem_mrblib_header(argv[2]); + else { + printf("%s", argument_info); + return 1; + } + } + else if (argc == 4) { + if (strcmp(argv[1], "gem_mrblib") == 0) + make_gem_mrblib(argv[2], argv[3]); + else if (strcmp(argv[1], "gem_srclib") == 0) + make_gem_srclib(argv[2], argv[3]); + else if (strcmp(argv[1], "gem_mixlib") == 0) + make_gem_mixlib(argv[2], argv[3]); + else { + printf("%s", argument_info); + return 1; + } + } + else { + printf("%s", argument_info); + return 1; + } + + return 0; +} diff --git a/src/init.c b/src/init.c index 663ebd68f..eb93fd0a7 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*); #define DONE mrb_gc_arena_restore(mrb, 0); void @@ -64,6 +65,8 @@ mrb_init_core(mrb_state *mrb) #ifdef ENABLE_MATH mrb_init_math(mrb); DONE; #endif - mrb_init_mrblib(mrb); DONE; +#ifdef ENABLE_GEMS + mrb_init_mrbgems(mrb); DONE; +#endif } diff --git a/test/Makefile b/test/Makefile index 3df07b280..dfbcd8ce2 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,9 +3,29 @@ # project-specific macros # extension of the executable-file is modifiable(.exe .out ...) +MRUBY_ROOT := .. BASEDIR = . TARGET := mrbtest LIBR := ../lib/libmruby.a + +ifeq ($(strip $(ENABLE_GEMS)),) + # by default GEMs are deactivated + ENABLE_GEMS = false +endif + +ifeq ($(ENABLE_GEMS),false) + GEM_ARCHIVE_FILES = +else + GEMDIR := ../mrbgems + GEMDLIB := $(GEMDIR)/g/mrbgemtest.ctmp + MAKEFILE_GEM_LIST := $(MRUBY_ROOT)/mrbgems/g/MakefileGemList + ifeq ($(wildcard $(MAKEFILE_GEM_LIST)),) + GEM_ARCHIVE_FILES = + else + include $(MAKEFILE_GEM_LIST) + endif +endif + MLIB := $(TARGET).o CLIB := $(TARGET).c INIT := init_$(TARGET).c @@ -58,7 +78,6 @@ MRUBY= ../bin/mruby EXE := $(TARGET) endif - ############################## # generic build targets, rules @@ -76,7 +95,7 @@ all : $(EXE) $(MRUBY) $(TESTRB) $(TESTMRB) # executable constructed using linker from object files $(EXE) : $(OBJS) $(LIBR) - $(LL) -o $@ $(CFLAGS) $(OBJS) $(LIBR) $(LIBS) + $(LL) -o $@ $(CFLAGS) $(OBJS) $(LIBR) $(GEM_ARCHIVE_FILES) $(LIBS) -include $(OBJS:.o=.d) @@ -85,8 +104,14 @@ $(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) +ifeq ($(ENABLE_GEMS),true) + @$(MAKE) prepare-test -C $(GEMDIR) +endif + $(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 e68c9d71a..47b14c325 100644 --- a/test/init_mrbtest.c +++ b/test/init_mrbtest.c @@ -5,11 +5,15 @@ #include "mruby/proc.h" extern const char mrbtest_irep[]; +extern const char mrbgemtest_irep[]; void mrb_init_mrbtest(mrb_state *mrb) { mrb_load_irep(mrb, mrbtest_irep); +#ifdef ENABLE_GEMS + mrb_load_irep(mrb, mrbgemtest_irep); +#endif if (mrb->exc) { mrb_p(mrb, mrb_obj_value(mrb->exc)); exit(0); diff --git a/tools/mirb/Makefile b/tools/mirb/Makefile index 8dbbc52b3..c44af4c6b 100644 --- a/tools/mirb/Makefile +++ b/tools/mirb/Makefile @@ -3,9 +3,27 @@ # project-specific macros # extension of the executable-file is modifiable(.exe .out ...) +MRUBY_ROOT := ../.. BASEDIR = ../../src TARGET := ../../bin/mirb LIBR := ../../lib/libmruby.a + +ifeq ($(strip $(ENABLE_GEMS)),) + # by default GEMs are deactivated + ENABLE_GEMS = false +endif + +ifeq ($(ENABLE_GEMS),false) + GEM_ARCHIVE_FILES = +else + MAKEFILE_GEM_LIST := $(MRUBY_ROOT)/mrbgems/g/MakefileGemList + ifeq ($(wildcard $(MAKEFILE_GEM_LIST)),) + GEM_ARCHIVE_FILES = + else + include $(MAKEFILE_GEM_LIST) + endif +endif + ifeq ($(OS),Windows_NT) EXE := $(TARGET).exe else @@ -49,7 +67,7 @@ all : $(LIBR) $(EXE) # executable constructed using linker from object files $(EXE) : $(LIBR) $(OBJS) $(EXTS) - $(LL) -o $@ $(CFLAGS) $(OBJS) $(LIBR) $(EXTS) $(LIBS) + $(LL) -o $@ $(CFLAGS) $(OBJS) $(LIBR) $(GEM_ARCHIVE_FILES) $(EXTS) $(LIBS) -include $(OBJS:.o=.d) diff --git a/tools/mrbc/mrbc.c b/tools/mrbc/mrbc.c index 2ecb00623..5a92cd479 100644 --- a/tools/mrbc/mrbc.c +++ b/tools/mrbc/mrbc.c @@ -225,3 +225,10 @@ void mrb_init_mrblib(mrb_state *mrb) { } + +#ifdef ENABLE_GEMS +void +mrb_init_mrbgems(mrb_state *mrb) +{ +} +#endif diff --git a/tools/mruby/Makefile b/tools/mruby/Makefile index e94c1b2b4..b31ed4941 100644 --- a/tools/mruby/Makefile +++ b/tools/mruby/Makefile @@ -3,9 +3,27 @@ # project-specific macros # extension of the executable-file is modifiable(.exe .out ...) -BASEDIR = ../../src -TARGET := ../../bin/mruby -LIBR := ../../lib/libmruby.a +MRUBY_ROOT := ../.. +BASEDIR = $(MRUBY_ROOT)/src +TARGET := $(MRUBY_ROOT)/bin/mruby +LIBR := $(MRUBY_ROOT)/lib/libmruby.a + +ifeq ($(strip $(ENABLE_GEMS)),) + # by default GEMs are deactivated + ENABLE_GEMS = false +endif + +ifeq ($(ENABLE_GEMS),false) + GEM_ARCHIVE_FILES = +else + MAKEFILE_GEM_LIST := $(MRUBY_ROOT)/mrbgems/g/MakefileGemList + ifeq ($(wildcard $(MAKEFILE_GEM_LIST)),) + GEM_ARCHIVE_FILES = + else + include $(MAKEFILE_GEM_LIST) + endif +endif + ifeq ($(OS),Windows_NT) EXE := $(TARGET).exe else @@ -54,7 +72,7 @@ all : $(LIBR) $(EXE) # executable constructed using linker from object files $(EXE) : $(LIBR) $(OBJS) $(EXTS) - $(LL) -o $@ $(CFLAGS) $(OBJS) $(LIBR) $(EXTS) $(LIBS) + $(LL) -o $@ $(CFLAGS) $(OBJS) $(LIBR) $(GEM_ARCHIVE_FILES) $(EXTS) $(LIBS) -include $(OBJS:.o=.d) |
