Embedding Perl in C 01-25-2018, 05:10 PM
#1
Perl is a great scripting language, and an easy one to embed in a C program if you need that kind of functionality.
Before we write any code, we need to setup our build environment. The first thing to do is create a Makefile, and add the following variables to it:
This gets the compiler that Perl is built with on your system, and the options needed to embed Perl using it. The "-O1" flag is to match the optimization level in ldflags.
Next, we add our rules. This is by all means a quick and dirty Makefile, so look up the proper way to do it if you intend to scale this.
Running "make build" (or just "make" since it's the default) will build our executable, and running "make clean" will delete it.
Now for the code. Create a directory called "lib", open a new file (name doesn't matter, as we used a wildcard for the compiler), and include these headers.
Next, we define our interpreter instance and create the skeleton of our main function (the name of the interpreter DOES matter, there's a hard-coded referenced in the PERL_SYS_INIT3 macro). And yes, that is a third (legacy) argument passed to main. It's been mostly obsolete since C89 due to getenv(), but it's useful for cases such as these.
Now we can create the interpreter instance and run our program:
Finally, we need to add a bit more to the cleanup section. Before the PERL_SYS_TERM() macro call, add the following to free any created instances.
Now we can build our program and pipe or redirect input to it, or provide a file to run.
Before we write any code, we need to setup our build environment. The first thing to do is create a Makefile, and add the following variables to it:
Code:
CFLAGS := -O1 $(shell perl -MExtUtils::Embed -eccopts,ldopts)
CC := $(shell perl -MConfig -e'print $$Config{cc}')
Next, we add our rules. This is by all means a quick and dirty Makefile, so look up the proper way to do it if you intend to scale this.
Code:
.PHONY: build clean
build: lib/*.c
$(CC) -o embed $< $(CFLAGS)
clean: embed
rm $<
Now for the code. Create a directory called "lib", open a new file (name doesn't matter, as we used a wildcard for the compiler), and include these headers.
Code:
#include <EXTERN.h> // perl extern
#include <perl.h> // perl API
Next, we define our interpreter instance and create the skeleton of our main function (the name of the interpreter DOES matter, there's a hard-coded referenced in the PERL_SYS_INIT3 macro). And yes, that is a third (legacy) argument passed to main. It's been mostly obsolete since C89 due to getenv(), but it's useful for cases such as these.
Code:
static PerlInterpreter *my_perl;
int main(int argc,char **argv,char **env){
// modify C runtime to run perl
PERL_SYS_INIT3(&argc,&argv,&env);
// rest of main body goes here
// restore C runtime
PERL_SYS_TERM();
return 0;
}
Now we can create the interpreter instance and run our program:
Code:
int main(int argc,char **argv,char **env){
// ...
// allocate for and create interpreter
my_perl=perl_alloc();
perl_construct(my_perl);
// add exit flags
PL_exit_flags|=PERL_EXIT_DESTRUCT_END;
// parse and interpret script
perl_parse(my_perl,NULL,argc,argv,(char**) NULL);
perl_run(my_perl);
// ...
}
Finally, we need to add a bit more to the cleanup section. Before the PERL_SYS_TERM() macro call, add the following to free any created instances.
Code:
int main(int argc,char **argv,char **env){
// ...
// destroy interpreter and free heap
perl_destroy(my_perl);
perl_free(my_perl);
// restore C runtime
PERL_SYS_TERM();
return 0;
}
Now we can build our program and pipe or redirect input to it, or provide a file to run.
Code:
$ make
...
$ cat hello.pl | ./embed
Hello, world!
$ ./embed <<< $(cat hello.pl)
Hello, world!
$ ./embed hello.pl
Hello, world!
It's often the outcasts, the iconoclasts ... those who have the least to lose because they
don't have much in the first place, who feel the new currents and ride them the farthest.
don't have much in the first place, who feel the new currents and ride them the farthest.