diff --git a/.gitignore b/.gitignore index a6258463a3..d7d66eac55 100644 --- a/.gitignore +++ b/.gitignore @@ -86,3 +86,6 @@ doc/gapmacrodoc.idx /hpcgap/ward /builds/ + +/libgap.la +/.libs diff --git a/.travis.yml b/.travis.yml index c8a1a06926..8df71eb1c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -108,6 +108,9 @@ matrix: # test error reporting and compiling (quickest job in this test suite) - env: TEST_SUITES="testspecial test-compile" + # test libgap + - env: TEST_SUITES="testlibgap" + script: - gcov --version - bash etc/ci-prepare.sh && bash etc/ci.sh diff --git a/Makefile.rules b/Makefile.rules index 7f785a2025..26c285a2d0 100644 --- a/Makefile.rules +++ b/Makefile.rules @@ -16,9 +16,6 @@ all: gap$(EXEEXT) gac .PHONY: all -libgap: libgap.la -.PHONY: libgap - # Backwards compatibility: add "default" target as alias for "all" default: all .PHONY: default @@ -64,6 +61,9 @@ SOURCES += src/intfuncs.c SOURCES += src/intrprtr.c SOURCES += src/io.c SOURCES += src/iostream.c +ifeq ($(HPCGAP),no) # we don't support a kernel API in HPC-GAP atm +SOURCES += src/libgap-api.c +endif SOURCES += src/listfunc.c SOURCES += src/listoper.c SOURCES += src/lists.c @@ -121,7 +121,6 @@ ifeq ($(HPCGAP),yes) SOURCES += src/hpc/traverse.c endif - ######################################################################## # Preprocessor flags # @@ -389,13 +388,16 @@ gap$(EXEEXT): libgap.la cnf/GAP-LDFLAGS cnf/GAP-LIBS cnf/GAP-OBJS else +all: libgap.so +libgap.so: symlinks libgap.la + $(QUIET_LINK)$(LINK) $(GAP_LDFLAGS) -shared $(OBJS) $(GAP_LIBS) -o $@ + # Linking rule and dependencies for the main gap executable gap$(EXEEXT): $(OBJS) cnf/GAP-LDFLAGS cnf/GAP-LIBS cnf/GAP-OBJS $(QUIET_LINK)$(LINK) $(GAP_LDFLAGS) $(OBJS) $(GAP_LIBS) -o $@ endif - ######################################################################## # The "docomp" target regenerates the various src/c_*.c files, and # replaces the old "etc/docomp" script. @@ -458,6 +460,7 @@ clean: rm -rf obj rm -f gap$(EXEEXT) gac ffgen rm -f libgap.la + rm -f libgap.so rm -f gen/gap_version.c rm -f doc/wsp.g rm -f cnf/GAP-{CFLAGS,CPPFLAGS,LDFLAGS,LIBS,OBJS} @@ -986,6 +989,12 @@ testbugfix: all ReadGapRoot( "tst/testbugfix.g" );' | $(TESTGAP) | \ tee `date -u +dev/log/testbugfix2_%Y-%m-%d-%H-%M` ) +testlibgap: libgap.la obj/tst/testlibgap/basic.lo + $(QUIET_LINK)$(LINK) $(GAP_LDFLAGS) obj/tst/testlibgap/basic.lo libgap.la -o test-libgap + ./test-libgap -A -l $(top_srcdir) -m 32m -q -T --nointeract > basic.out + diff $(top_srcdir)/tst/testlibgap/basic.expect basic.out +.PHONY: testlibgap + coverage: gcov -o . $(SOURCES) diff --git a/etc/ci.sh b/etc/ci.sh index d71651b268..a66edbab6f 100644 --- a/etc/ci.sh +++ b/etc/ci.sh @@ -177,6 +177,10 @@ GAPInput ;; + testlibgap) + make testlibgap + ;; + *) if [[ ! -f $SRCDIR/tst/${TEST_SUITE}.g ]] then diff --git a/lib/streams.gi b/lib/streams.gi index d737dde092..af00393a78 100644 --- a/lib/streams.gi +++ b/lib/streams.gi @@ -234,14 +234,6 @@ function( stream ) CloseStream(stream); end ); -BindGlobal("LIBGAP_EvalString", -function(string) - local instream, obj; - instream := InputTextString(string); - obj := READ_ALL_COMMANDS(instream, 0, false); - return obj; -end); - ############################################################################# ## diff --git a/src/libgap-api.c b/src/libgap-api.c new file mode 100644 index 0000000000..82cc441711 --- /dev/null +++ b/src/libgap-api.c @@ -0,0 +1,62 @@ +// LibGAP API - API for using GAP as shared library. + +#include "libgap-api.h" + +#include "bool.h" +#include "opers.h" +#include "calls.h" +#include "gapstate.h" +#include "gvars.h" +#include "lists.h" +#include "streams.h" +#include "stringobj.h" + +// +// Setup and initialisation +// +void GAP_Initialize(int argc, + char ** argv, + char ** env, + CallbackFunc markBagsCallback, + CallbackFunc errorCallback) +{ + InitializeGap(&argc, argv, env); + SetExtraMarkFuncBags(markBagsCallback); + STATE(JumpToCatchCallback) = errorCallback; +} + + +// Combines GVarName and ValGVar. For a given string, it returns the value +// of the gvar with name , or NULL if the global variable is not +// defined. +Obj GAP_ValueGlobalVariable(const char * name) +{ + UInt gvar = GVarName(name); + // TODO: GVarName should never return 0? + if (gvar != 0) { + return ValGVar(gvar); + } + else { + return NULL; + } +} + +// +// Evaluate a string of GAP commands +// +// To see an example of how to use this function +// see tst/testlibgap/basic.c +// +Obj GAP_EvalString(const char * cmd) +{ + Obj instream; + Obj res; + Obj viewObjFunc, streamFunc; + + streamFunc = GAP_ValueGlobalVariable("InputTextString"); + viewObjFunc = GAP_ValueGlobalVariable("ViewObj"); + + instream = DoOperation1Args(streamFunc, MakeString(cmd)); + res = READ_ALL_COMMANDS(instream, False, True, viewObjFunc); + return res; +} diff --git a/src/libgap-api.h b/src/libgap-api.h new file mode 100644 index 0000000000..e45d6fc91d --- /dev/null +++ b/src/libgap-api.h @@ -0,0 +1,21 @@ +// LibGAP API - API for using GAP as shared library. + +#ifndef LIBGAP_API_H +#define LIBGAP_API_H + +#include "gap.h" + +typedef void (*CallbackFunc)(void); + +// Initialisation and finalization + +void GAP_Initialize(int argc, + char ** argv, + char ** env, + CallbackFunc markBagsCallback, + CallbackFunc errorCallback); + +Obj GAP_ValueGlobalVariable(const char * name); +Obj GAP_EvalString(const char * cmd); + +#endif diff --git a/tst/testlibgap/basic.c b/tst/testlibgap/basic.c new file mode 100644 index 0000000000..08cd9f92db --- /dev/null +++ b/tst/testlibgap/basic.c @@ -0,0 +1,77 @@ +/* + * Small program to test libgap linkability and basic working + */ +#include +#include +#include +#include +extern char ** environ; + +UInt GAP_List_Length(Obj list) +{ + return LEN_LIST(list); +} + +Obj GAP_List_AtPosition(Obj list, Int pos) +{ + return ELM_LIST(list, pos); +} + +UInt GAP_String_Length(Obj string) +{ + return GET_LEN_STRING(string); +} + +Int GAP_String_GetCString(Obj string, Char * buffer, UInt n) +{ + UInt len; + + if (IS_STRING(string)) { + if (!IS_STRING_REP(string)) + string = CopyToStringRep(string); + len = GET_LEN_STRING(string) + 1; + if (len >= n) + len = n - 1; + // Have to use mempcy because GAP strings can contain + // \0. + memcpy(buffer, CSTR_STRING(string), len); + if (len == n - 1) + buffer[n] = '\0'; + return 1; + } + return 0; +} + +void test_eval(const char * cmd) +{ + Obj res, ires; + Int rc, i; + Char buffer[4096]; + printf("gap> %s\n", cmd); + res = GAP_EvalString(cmd); + rc = GAP_List_Length(res); + for (i = 1; i <= rc; i++) { + ires = GAP_List_AtPosition(res, i); + if (GAP_List_AtPosition(ires, 1) == True) { + GAP_String_GetCString(GAP_List_AtPosition(ires, 5), buffer, + sizeof(buffer)); + printf("%s\n", buffer); + } + } +} +int main(int argc, char ** argv) +{ + printf("# Initializing GAP...\n"); + GAP_Initialize(argc, argv, environ, 0L, 0L); + CollectBags(0, 1); // full GC + test_eval("1+2+3;"); + test_eval("g:=FreeGroup(2);"); + test_eval("a:=g.1;"); + test_eval("b:=g.2;"); + test_eval("lis:=[a^2, a^2, b*a];"); + test_eval("h:=g/lis;"); + test_eval("c:=h.1;"); + test_eval("Set([1..1000000], i->Order(c));"); + printf("# done\n"); + return 0; +} diff --git a/tst/testlibgap/basic.expect b/tst/testlibgap/basic.expect new file mode 100644 index 0000000000..5ce9634d9e --- /dev/null +++ b/tst/testlibgap/basic.expect @@ -0,0 +1,18 @@ +# Initializing GAP... +gap> 1+2+3; +6 +gap> g:=FreeGroup(2); + +gap> a:=g.1; +f1 +gap> b:=g.2; +f2 +gap> lis:=[a^2, a^2, b*a]; +[ f1^2, f1^2, f2*f1 ] +gap> h:=g/lis; + +gap> c:=h.1; +f1 +gap> Set([1..1000000], i->Order(c)); +[ 2 ] +# done