| # -*-perl-*- | |
| $description = "Test the shared object load API."; | |
| $details = "Verify the different aspects of the shared object API."; | |
| # Don't do anything if this system doesn't support "load" | |
| exists $FEATURES{load} or return -1; | |
| my $cc = get_config('CC'); | |
| if (! $cc) { | |
| $verbose and print "Skipping load test: no CC defined\n"; | |
| return -1; | |
| } | |
| # First build a shared object | |
| # Provide both a default and non-default load symbol | |
| unlink(qw(testapi.c testapi.so)); | |
| open(my $F, '> testapi.c') or die "open: testapi.c: $!\n"; | |
| print $F <<'EOF' ; | |
| #include <string.h> | |
| #include <stdio.h> | |
| #include "gnumake.h" | |
| char *getenv (const char*); | |
| int plugin_is_GPL_compatible; | |
| int testapi_gmk_setup (unsigned int abi, const gmk_floc *floc); | |
| static char * | |
| test_eval (const char *buf) | |
| { | |
| gmk_eval (buf, 0); | |
| return NULL; | |
| } | |
| static char * | |
| test_expand (const char *val) | |
| { | |
| return gmk_expand (val); | |
| } | |
| static char * | |
| test_noexpand (const char *val) | |
| { | |
| char *str = gmk_alloc (strlen (val) + 1); | |
| strcpy (str, val); | |
| return str; | |
| } | |
| static char * | |
| func_test (const char *funcname, unsigned int argc, char **argv) | |
| { | |
| char *mem; | |
| (void)argc; | |
| if (strcmp (funcname, "test-expand") == 0) | |
| return test_expand (argv[0]); | |
| if (strcmp (funcname, "test-eval") == 0) | |
| return test_eval (argv[0]); | |
| if (strcmp (funcname, "test-noexpand") == 0) | |
| return test_noexpand (argv[0]); | |
| mem = gmk_alloc (sizeof ("unknown")); | |
| strcpy (mem, "unknown"); | |
| return mem; | |
| } | |
| int | |
| testapi_gmk_setup (unsigned int abi, const gmk_floc *floc) | |
| { | |
| const char *verbose = getenv ("TESTAPI_VERBOSE"); | |
| (void)abi; | |
| gmk_add_function ("test-expand", func_test, 1, 1, GMK_FUNC_DEFAULT); | |
| gmk_add_function ("test-noexpand", func_test, 1, 1, GMK_FUNC_NOEXPAND); | |
| gmk_add_function ("test-eval", func_test, 1, 1, GMK_FUNC_DEFAULT); | |
| gmk_add_function ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.", func_test, 0, 0, 0); | |
| if (verbose) | |
| { | |
| printf ("testapi_gmk_setup\n"); | |
| if (verbose[0] == '2') | |
| printf ("%s:%lu\n", floc->filenm, floc->lineno); | |
| } | |
| if (getenv ("TESTAPI_KEEP")) | |
| return -1; | |
| return 1; | |
| } | |
| int | |
| alternative_setup () | |
| { | |
| gmk_add_function ("test-expand", func_test, 1, 1, GMK_FUNC_DEFAULT); | |
| gmk_add_function ("test-noexpand", func_test, 1, 1, GMK_FUNC_NOEXPAND); | |
| gmk_add_function ("test-eval", func_test, 1, 1, GMK_FUNC_DEFAULT); | |
| gmk_add_function ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.", func_test, 0, 0, 0); | |
| if (getenv ("TESTAPI_VERBOSE")) | |
| printf ("alternative_setup\n"); | |
| if (getenv ("TESTAPI_KEEP")) | |
| return -1; | |
| return 1; | |
| } | |
| void | |
| testapi_gmk_unload () | |
| { | |
| const char *s = getenv ("TESTAPI_VERBOSE"); | |
| if (s && *s == '3') | |
| printf ("testapi_gmk_unload\n"); | |
| } | |
| EOF | |
| close($F) or die "close: testapi.c: $!\n"; | |
| # Make sure we can compile | |
| my $cflags = get_config('CFLAGS'); | |
| my $cppflags = get_config('CPPFLAGS'); | |
| my $ldflags = get_config('LDFLAGS'); | |
| my $sobuild = "$cc ".($srcdir? "-I$srcdir/src":'')." $cppflags $cflags -shared -fPIC $ldflags -o testapi.so testapi.c"; | |
| my $clog = `$sobuild 2>&1`; | |
| if ($? != 0) { | |
| $verbose and print "Failed to build testapi.so:\n$sobuild\n$_"; | |
| return -1; | |
| } | |
| # TEST 1 | |
| # Check the gmk_expand() function | |
| run_make_test(q! | |
| EXPAND = expansion | |
| all: ; @echo $(test-expand $$(EXPAND)) | |
| load testapi.so | |
| !, | |
| '', "expansion\n"); | |
| # TEST 2 | |
| # Check the eval operation. Prove that the argument is expanded only once | |
| run_make_test(q! | |
| load testapi.so | |
| TEST = bye | |
| ASSIGN = VAR = $(TEST) $(shell echo there) | |
| $(test-eval $(value ASSIGN)) | |
| TEST = hi | |
| all:;@echo '$(VAR)' | |
| !, | |
| '', "hi there\n"); | |
| # TEST 2 | |
| # Check the no-expand capability | |
| run_make_test(q! | |
| load testapi.so | |
| TEST = hi | |
| all:;@echo '$(test-noexpand $(TEST))' | |
| !, | |
| '', "\$(TEST)\n"); | |
| # During all subsequent tests testapi.so exists. | |
| # | |
| my @loads = ('', q! | |
| load testapi.so | |
| load testapi.so | |
| -load testapi.so | |
| -load testapi.so | |
| $(eval load testapi.so) | |
| $(eval -load testapi.so) | |
| !); | |
| for my $extra_loads (@loads) { | |
| my $n = 5; | |
| if ($extra_loads) { | |
| $n = 12; | |
| } | |
| # sv 63045. | |
| # Test that having unloaded a shared object make loads it again, even if the | |
| # shared object is not updated. | |
| $ENV{TESTAPI_VERBOSE} = 1; | |
| run_make_test(" | |
| load testapi.so | |
| $extra_loads | |
| all:; \$(info \$(test-expand hello)) | |
| testapi.so: force; \$(info \$@) | |
| force:; | |
| .PHONY: force | |
| ", '', "testapi_gmk_setup\ntestapi.so\ntestapi_gmk_setup\nhello\n#MAKE#: 'all' is up to date.\n"); | |
| # sv 63045. | |
| # Same as above, but testapi_gmk_setup returned -1. | |
| $ENV{TESTAPI_KEEP} = 1; | |
| $ENV{TESTAPI_VERBOSE} = 1; | |
| run_make_test(" | |
| load testapi.so | |
| $extra_loads | |
| all:; \$(info \$(test-expand hello)) | |
| testapi.so: force; \$(info \$@) | |
| force:; | |
| .PHONY: force | |
| ", '', "testapi_gmk_setup\nhello\n#MAKE#: 'all' is up to date.\n"); | |
| # sv 63045. | |
| # Test that make exits, unless make can successfully update an unloaded shared | |
| # object. | |
| $ENV{TESTAPI_VERBOSE} = 1; | |
| run_make_test(" | |
| load testapi.so | |
| $extra_loads | |
| all:; \$(info \$(test-expand hello)) | |
| testapi.so: force; @#HELPER# fail 1 | |
| force:; | |
| .PHONY: force | |
| ", '', "testapi_gmk_setup\nfail 1\n#MAKE#: *** [#MAKEFILE#:$n: testapi.so] Error 1\n", 512); | |
| # sv 63045. | |
| # Same as above, but testapi_gmk_setup returned -1. | |
| $ENV{TESTAPI_KEEP} = 1; | |
| $ENV{TESTAPI_VERBOSE} = 1; | |
| run_make_test(" | |
| load testapi.so | |
| $extra_loads | |
| all:; \$(info \$(test-expand hello)) | |
| testapi.so: force; @#HELPER# fail 1 | |
| force:; | |
| .PHONY: force | |
| ", '', "testapi_gmk_setup\nhello\n#MAKE#: 'all' is up to date.\n"); | |
| # sv 63100. | |
| # Test that make supplies the correct floc when the shared object is loaded | |
| # again. | |
| $ENV{TESTAPI_VERBOSE} = 2; | |
| run_make_test(" | |
| load testapi.so | |
| $extra_loads | |
| all:; \$(info \$(test-expand hello)) | |
| testapi.so: force; \$(info \$@) | |
| force:; | |
| .PHONY: force | |
| ", '', "testapi_gmk_setup\n#MAKEFILE#:2\ntestapi.so\ntestapi_gmk_setup\n#MAKEFILE#:2\nhello\n#MAKE#: 'all' is up to date.\n"); | |
| } | |
| my @names = ('testapi.so', './testapi.so', '#PWD#/testapi.so'); | |
| for my $name (@names) { | |
| # Test the make correctly figures out the name of the close function and runs | |
| # the close function. | |
| $ENV{TESTAPI_VERBOSE} = 3; | |
| run_make_test(" | |
| load $name | |
| all:; \$(info \$(test-expand hello)) | |
| ", '', "testapi_gmk_setup\nhello\n#MAKE#: 'all' is up to date.\ntestapi_gmk_unload\n"); | |
| } | |
| # Same as above, but the setup function is custom. | |
| @names = ('testapi.so(alternative_setup)', './testapi.so(alternative_setup)', | |
| '#PWD#/testapi.so(alternative_setup)'); | |
| for my $name (@names) { | |
| # Test the close function. | |
| $ENV{TESTAPI_VERBOSE} = 3; | |
| run_make_test(" | |
| load $name | |
| all:; \$(info \$(test-expand hello)) | |
| ", '', "alternative_setup\nhello\n#MAKE#: 'all' is up to date.\ntestapi_gmk_unload\n"); | |
| } | |
| # Test that makes runs the close function on failure. | |
| $ENV{TESTAPI_VERBOSE} = 3; | |
| run_make_test(q! | |
| load testapi.so | |
| all: bad_preqreq; : | |
| !, '', "testapi_gmk_setup\n#MAKE#: *** No rule to make target 'bad_preqreq', needed by 'all'. Stop.\ntestapi_gmk_unload\n", 512); | |
| # Test that make unloads a shared object, calls the close function, loads | |
| # the plugin again, and then calls the close function again on exit. | |
| &utouch(-10, 'testapi.so'); | |
| $ENV{TESTAPI_VERBOSE} = 3; | |
| run_make_test(" | |
| load testapi.so | |
| all:; \$(info \$(test-expand hello)) | |
| testapi.so: testapi.c; $sobuild | |
| ", '', "testapi_gmk_setup\ntestapi_gmk_unload\n$sobuild\ntestapi_gmk_setup\nhello\n#MAKE#: 'all' is up to date.\ntestapi_gmk_unload"); | |
| # Test that make unloads a shared object, calls the close function, loads | |
| # the plugin again, and then calls the close function again on exit. | |
| $ENV{TESTAPI_VERBOSE} = 3; | |
| run_make_test(q! | |
| load testapi.so | |
| all:; $(info $(test-expand hello)) | |
| testapi.so: force; $(info $@) | |
| force:; | |
| .PHONY: force | |
| !, '', "testapi_gmk_setup\ntestapi_gmk_unload\ntestapi.so\ntestapi_gmk_setup\nhello\n#MAKE#: 'all' is up to date.\ntestapi_gmk_unload\n"); | |
| unlink(qw(testapi.c testapi.so)) unless $keep; | |
| # Test that make does not run the close function, unless the shared object | |
| # loaded successfully. | |
| unlink('testapi.so'); | |
| $ENV{TESTAPI_VERBOSE} = 3; | |
| run_make_test(q! | |
| load testapi.so | |
| all:; : | |
| !, '', "#MAKEFILE#:2: testapi.so: cannot open shared object file: $ERR_no_such_file\n#MAKEFILE#:2: *** testapi.so: failed to load. Stop.\n", 512); | |
| # This tells the test driver that the perl test script executed properly. | |
| 1; |