如何用ChezScheme生成单一可执行文件
Category:软件发布自从Matthew Flatt的这个PR之后,用ChezScheme生成单一的可执行文件是非常简单的(在此之前,会需要临时文件)。但就我观察,似乎仍有很多人不知道怎么弄的样子,这里做个简单的示范,系统是Ubuntu 16.04,其他系统请自行调整代码。
首先编写一个类似如下的C程序(错误处理略),名字随意,比如main.c
#include "scheme.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
int main(int argc, char *argv[])
{
Sscheme_init(NULL);
int self = open(argv[0], O_RDONLY);
uint64_t offs[3];
lseek(self, -24, SEEK_END);
read(self, offs, 24);
int p = open(argv[0], O_RDONLY);
lseek(p, offs[0], SEEK_SET);
Sregister_boot_file_fd("petite", p);
int s = open(argv[0], O_RDONLY);
lseek(s, offs[1], SEEK_SET);
Sregister_boot_file_fd("scheme", s);
lseek(self, offs[2], SEEK_SET);
Sregister_boot_file_fd("self", self);
Sbuild_heap(argv[0], NULL);
Scall0(Stop_level_value(Sstring_to_symbol("main")));
Sscheme_deinit();
return 0;
}
编译,得到一个a.out(路径请按实际情况修改):
gcc main.c ChezScheme/ta6le/boot/ta6le/kernel.o -IChezScheme/ta6le/boot/ta6le/ -lpthread -lncurses -luuid -ldl -lm
然后编写一个打包用的Scheme程序(pack.ss),boot路径名请自行修改
(define (call/inputs files proc)
(let rec ([files (reverse files)] [ports '()])
(if (null? files)
(apply proc ports)
(let ([p (open-file-input-port (car files) (file-options) (buffer-mode block) #f)])
(rec (cdr files) (cons p ports))
(close-port p)))))
(define (pack a.out boot)
(call/inputs
(list a.out boot "ChezScheme/ta6le/boot/ta6le/petite.boot"
"ChezScheme/ta6le/boot/ta6le/scheme.boot")
(lambda (a.out boot petite scheme)
(let ([s1 (file-length a.out)]
[s2 (file-length petite)]
[s3 (file-length scheme)]
[s4 (file-length boot)]
[shuffix (make-bytevector 24)])
(bytevector-u64-native-set! shuffix 0 s1)
(bytevector-u64-native-set! shuffix 8 (+ s1 s2))
(bytevector-u64-native-set! shuffix 16 (+ s1 s2 s3))
(let ([b1 (get-bytevector-all a.out)]
[b2 (get-bytevector-all petite)]
[b3 (get-bytevector-all scheme)]
[b4 (get-bytevector-all boot)])
(let ([p (open-file-output-port "main" (file-options no-fail) (buffer-mode block) #f)])
(put-bytevector p b1)
(put-bytevector p b2)
(put-bytevector p b3)
(put-bytevector p b4)
(put-bytevector p shuffix)
(close-port p)))))))
现在所有准备工作都完成了
随便写个简单的Scheme程序,文件名就叫foo.ss好了:
(define (main)
(let loop ()
(display "->")
(let ([o (read)])
(unless (eof-object? o)
(display (eval o))
(newline)
(loop)))))
然后生成boot文件:
> (compile-file "foo.ss")
compiling foo.ss with output to foo.so
> (make-boot-file "foo.boot" '("petite") "foo.so")
>
打包
> (load "pack.ss")
> (pack "a.out" "foo.boot")
>
然后来试验一下吧:
$ ./main
->(#%$closure-ref (let ([x #f]) (set! x "qww6") (lambda () x)) 0)
(qww6 . #<unbound object>)
->
就是这样了,当然自由发挥空间还有很多:例如如果不需要动态编译代码,那么scheme.boot可以不带,可以像Racket on Chez那样使用字符串hack等,这里不一一列举。
发布于 2018-10-29
http://mip.i3geek.com