diff --git a/.gitignore b/.gitignore index a6258463a38..d7d66eac556 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 c8a1a069261..bd3fb8caaca 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" CONFIGFLAGS="--enable-debug --enable-Werror" + script: - gcov --version - bash etc/ci-prepare.sh && bash etc/ci.sh diff --git a/Makefile.rules b/Makefile.rules index 7f785a20259..739e95bc167 100644 --- a/Makefile.rules +++ b/Makefile.rules @@ -16,7 +16,7 @@ all: gap$(EXEEXT) gac .PHONY: all -libgap: libgap.la +libgap: libgap.la sysinfo.gap symlinks .PHONY: libgap # Backwards compatibility: add "default" target as alias for "all" @@ -64,6 +64,7 @@ SOURCES += src/intfuncs.c SOURCES += src/intrprtr.c SOURCES += src/io.c SOURCES += src/iostream.c +SOURCES += src/libgap-api.c SOURCES += src/listfunc.c SOURCES += src/listoper.c SOURCES += src/lists.c @@ -121,7 +122,6 @@ ifeq ($(HPCGAP),yes) SOURCES += src/hpc/traverse.c endif - ######################################################################## # Preprocessor flags # @@ -395,6 +395,8 @@ gap$(EXEEXT): $(OBJS) cnf/GAP-LDFLAGS cnf/GAP-LIBS cnf/GAP-OBJS endif +libgap.so: $(OBJS) + $(QUIET_LINK)$(LINK) $(GAP_LDFLAGS) -shared $(OBJS) $(GAP_LIBS) -o $@ ######################################################################## # The "docomp" target regenerates the various src/c_*.c files, and @@ -986,6 +988,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 -l $(top_srcdir) -m 32m -q -T > 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 d71651b268e..a66edbab6fe 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/init.g b/lib/init.g index bb5f570b201..fdd301db9c4 100644 --- a/lib/init.g +++ b/lib/init.g @@ -1042,11 +1042,12 @@ if IsLIBGAP then elif IsHPCGAP and THREAD_UI() then ReadLib("hpc/consoleui.g"); MULTI_SESSION(); -else + PROGRAM_CLEAN_UP(); +elif not IsLIBGAP then SESSION(); + PROGRAM_CLEAN_UP(); fi; -PROGRAM_CLEAN_UP(); ############################################################################# diff --git a/lib/streams.gi b/lib/streams.gi index d737dde0927..af00393a78d 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 00000000000..9885f6fac48 --- /dev/null +++ b/src/libgap-api.c @@ -0,0 +1,95 @@ +// 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; +} + + +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) +{ + Obj copy; + UInt len; + + if (IS_STRING(string)) { + copy = CopyToStringRep(string); + len = GET_LEN_STRING(copy) + 1; + if (len >= n) + len = n - 1; + // Have to use mempcy because GAP strings can contain + // \0. + memcpy(buffer, CSTR_STRING(copy), len); + if (len == n - 1) + buffer[n] = '\0'; + return 1; + } + return 0; +} + + +// 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 +// +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 00000000000..3a5796ec56b --- /dev/null +++ b/src/libgap-api.h @@ -0,0 +1,48 @@ +// LibGAP API - API for using GAP as shared library. +// +// WARNING +// +// This API is work in progress, hence subject to change. +// +// We currently only provide a small amount of functionality +// via a well-defined API that wraps internal GAP functions. +// +// Users of GAP as a library currently call directly into +// GAP kernel functions. In the future we would like to hide +// all GAP internal functions (in the .so/.dll file) and only +// allow access to GAP via functions defined in this file. +// +// If you have a request to expose functionality via the API +// please contact us via the GAP mailing list or github +// issue tracker. +// + +#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); + +// List operations. These are currently here like this, because they are used +// in +// testlibgap. +UInt GAP_List_Length(Obj list); +Obj GAP_List_AtPosition(Obj list, Int pos); + +UInt GAP_String_Length(Obj string); +Int GAP_String_GetCString(Obj string, Char * buffer, UInt max); + +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 00000000000..ccb0c600051 --- /dev/null +++ b/tst/testlibgap/basic.c @@ -0,0 +1,41 @@ +/* + * Small program to test libgap linkability and basic working + */ +#include +#include +#include +#include +extern char ** environ; +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 00000000000..b5cb943afd0 --- /dev/null +++ b/tst/testlibgap/basic.expect @@ -0,0 +1,19 @@ +# 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