Add ability to create standalone binaries with qjs
This commit is contained in:
parent
136f5a2c66
commit
8759581866
1 changed files with 157 additions and 1 deletions
158
qjs.c
158
qjs.c
|
@ -43,6 +43,112 @@ extern const uint32_t qjsc_repl_size;
|
||||||
|
|
||||||
static JSCFunctionListEntry argv0;
|
static JSCFunctionListEntry argv0;
|
||||||
|
|
||||||
|
static const char trailer_magic[] = "1qu1ckJS";
|
||||||
|
static const int trailer_magic_size = sizeof(trailer_magic) - 1;
|
||||||
|
static const int trailer_size = 16; /* 8 bytes for the trailer magic + 6 bytes for the offset. */
|
||||||
|
|
||||||
|
static int compile_standalone(const char *exe, const char *js_file, const char *out) {
|
||||||
|
FILE *exe_f = fopen(exe, "rb");
|
||||||
|
if (!exe_f) {
|
||||||
|
perror(exe);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (fseek(exe_f, 0, SEEK_END) < 0) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
size_t exe_size = ftell(exe_f);
|
||||||
|
rewind(exe_f);
|
||||||
|
|
||||||
|
FILE *js_f = fopen(js_file, "rb");
|
||||||
|
if (!js_f) {
|
||||||
|
perror(js_file);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (fseek(js_f, 0, SEEK_END) < 0) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
size_t js_size = ftell(js_f);
|
||||||
|
rewind(js_f);
|
||||||
|
|
||||||
|
size_t buf_size = exe_size + js_size + trailer_size;
|
||||||
|
uint8_t *buf = malloc(buf_size);
|
||||||
|
if (!buf) {
|
||||||
|
perror("malloc");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fread(buf, 1, exe_size, exe_f) != exe_size) {
|
||||||
|
perror("fread exe");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fread(buf+exe_size, 1, js_size, js_f) != js_size) {
|
||||||
|
perror("fread js");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buf+exe_size+js_size, trailer_magic, trailer_magic_size);
|
||||||
|
memcpy(buf+exe_size+js_size+trailer_magic_size, &exe_size, sizeof(uint64_t));
|
||||||
|
|
||||||
|
FILE *out_f = fopen(out, "wb");
|
||||||
|
if (!out_f) {
|
||||||
|
perror(out);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(buf, 1, buf_size, out_f) != buf_size) {
|
||||||
|
perror("fwrite");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
fclose(exe_f);
|
||||||
|
fclose(js_f);
|
||||||
|
fclose(out_f);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t* read_trailer(const char *exe, size_t *bsize) {
|
||||||
|
FILE *exe_f = fopen(exe, "rb");
|
||||||
|
if (!exe_f) {
|
||||||
|
perror(exe);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (fseek(exe_f, -trailer_size, SEEK_END) < 0) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
size_t trailer_start = ftell(exe_f);
|
||||||
|
uint8_t buf[trailer_size];
|
||||||
|
if (fread(buf, 1, trailer_size, exe_f) != trailer_size) {
|
||||||
|
perror("fread exe trailer");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(buf, trailer_magic, trailer_magic_size)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
uint64_t offset;
|
||||||
|
memcpy(&offset, buf+trailer_magic_size, sizeof(uint64_t));
|
||||||
|
|
||||||
|
if (fseek(exe_f, offset, SEEK_SET) < 0) {
|
||||||
|
perror("fseek to offset");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bundle_size = trailer_start - offset;
|
||||||
|
uint8_t *bundle = malloc(bundle_size);
|
||||||
|
if (fread(bundle, 1, bundle_size, exe_f) != bundle_size) {
|
||||||
|
perror("fread exe bundle");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(exe_f);
|
||||||
|
|
||||||
|
*bsize = bundle_size;
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
|
static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
|
||||||
const char *filename, int eval_flags)
|
const char *filename, int eval_flags)
|
||||||
{
|
{
|
||||||
|
@ -297,12 +403,14 @@ void help(void)
|
||||||
printf("QuickJS-ng version %s\n"
|
printf("QuickJS-ng version %s\n"
|
||||||
"usage: " PROG_NAME " [options] [file [args]]\n"
|
"usage: " PROG_NAME " [options] [file [args]]\n"
|
||||||
"-h --help list options\n"
|
"-h --help list options\n"
|
||||||
|
"-c --compile FILE compiles the given file into an executable\n"
|
||||||
"-e --eval EXPR evaluate EXPR\n"
|
"-e --eval EXPR evaluate EXPR\n"
|
||||||
"-i --interactive go to interactive mode\n"
|
"-i --interactive go to interactive mode\n"
|
||||||
"-m --module load as ES6 module (default=autodetect)\n"
|
"-m --module load as ES6 module (default=autodetect)\n"
|
||||||
" --script load as ES6 script (default=autodetect)\n"
|
" --script load as ES6 script (default=autodetect)\n"
|
||||||
"-I --include file include an additional file\n"
|
"-I --include file include an additional file\n"
|
||||||
" --std make 'std' and 'os' available to the loaded script\n"
|
" --std make 'std' and 'os' available to the loaded script\n"
|
||||||
|
"-o --output file output file name when using -c\n"
|
||||||
"-T --trace trace memory allocation\n"
|
"-T --trace trace memory allocation\n"
|
||||||
"-d --dump dump the memory usage stats\n"
|
"-d --dump dump the memory usage stats\n"
|
||||||
" --memory-limit n limit the memory usage to 'n' Kbytes\n"
|
" --memory-limit n limit the memory usage to 'n' Kbytes\n"
|
||||||
|
@ -318,6 +426,10 @@ int main(int argc, char **argv)
|
||||||
JSContext *ctx;
|
JSContext *ctx;
|
||||||
struct trace_malloc_data trace_data = { NULL };
|
struct trace_malloc_data trace_data = { NULL };
|
||||||
int optind;
|
int optind;
|
||||||
|
uint8_t *bundle = NULL;
|
||||||
|
size_t bundle_size = 0;
|
||||||
|
char *compile_file = NULL;
|
||||||
|
char *out_file = NULL;
|
||||||
char *expr = NULL;
|
char *expr = NULL;
|
||||||
int interactive = 0;
|
int interactive = 0;
|
||||||
int dump_memory = 0;
|
int dump_memory = 0;
|
||||||
|
@ -335,6 +447,13 @@ int main(int argc, char **argv)
|
||||||
argv0 = (JSCFunctionListEntry)JS_PROP_STRING_DEF("argv0", argv[0],
|
argv0 = (JSCFunctionListEntry)JS_PROP_STRING_DEF("argv0", argv[0],
|
||||||
JS_PROP_C_W_E);
|
JS_PROP_C_W_E);
|
||||||
|
|
||||||
|
bundle = read_trailer(argv[0], &bundle_size);
|
||||||
|
if (bundle) {
|
||||||
|
/* Skip all options. */
|
||||||
|
optind = 1;
|
||||||
|
goto start;
|
||||||
|
}
|
||||||
|
|
||||||
/* cannot use getopt because we want to pass the command line to
|
/* cannot use getopt because we want to pass the command line to
|
||||||
the script */
|
the script */
|
||||||
optind = 1;
|
optind = 1;
|
||||||
|
@ -367,6 +486,28 @@ int main(int argc, char **argv)
|
||||||
help();
|
help();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (opt == 'c' || !strcmp(longopt, "compile")) {
|
||||||
|
if (!opt_arg) {
|
||||||
|
if (optind >= argc) {
|
||||||
|
fprintf(stderr, "qjs: missing file for -c\n");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
opt_arg = argv[optind++];
|
||||||
|
}
|
||||||
|
compile_file = opt_arg;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (opt == 'o' || !strcmp(longopt, "output")) {
|
||||||
|
if (!opt_arg) {
|
||||||
|
if (optind >= argc) {
|
||||||
|
fprintf(stderr, "qjs: missing file for -o\n");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
opt_arg = argv[optind++];
|
||||||
|
}
|
||||||
|
out_file = opt_arg;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (opt == 'e' || !strcmp(longopt, "eval")) {
|
if (opt == 'e' || !strcmp(longopt, "eval")) {
|
||||||
if (!opt_arg) {
|
if (!opt_arg) {
|
||||||
if (optind >= argc) {
|
if (optind >= argc) {
|
||||||
|
@ -457,6 +598,18 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (compile_file) {
|
||||||
|
if (!out_file) {
|
||||||
|
fprintf(stderr, "qjs: output file must be specified\n");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
compile_standalone(argv[0], compile_file, out_file);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
start:
|
||||||
if (trace_memory) {
|
if (trace_memory) {
|
||||||
js_trace_malloc_init(&trace_data);
|
js_trace_malloc_init(&trace_data);
|
||||||
rt = JS_NewRuntime2(&trace_mf, &trace_data);
|
rt = JS_NewRuntime2(&trace_mf, &trace_data);
|
||||||
|
@ -506,7 +659,10 @@ int main(int argc, char **argv)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expr) {
|
if (bundle) {
|
||||||
|
if (eval_buf(ctx, bundle, bundle_size, "<bundle>", 0))
|
||||||
|
goto fail;
|
||||||
|
} else if (expr) {
|
||||||
if (eval_buf(ctx, expr, strlen(expr), "<cmdline>", 0))
|
if (eval_buf(ctx, expr, strlen(expr), "<cmdline>", 0))
|
||||||
goto fail;
|
goto fail;
|
||||||
} else
|
} else
|
||||||
|
|
Loading…
Reference in a new issue