cl-launch - 用于 Common Lisp 的 shell 包装器

cl-launch - 用于 Common Lisp 的 shell 包装器

概要


cl [options] '(lisp form to evaluate)'
    求值指定的 form, 在换行后面打印结果

cl [options] script-file arguments... 
    运行指定的脚本,传递参数

cl [options] [--execute] [options] [-- arguments...]

cl [options] --output EXECUTABLE [options]
    生成可执行的脚本文件或二进制文件

特殊模式


-h  or -?     --help                显示简短的帮助信息
-H            --more-help           显示完整的帮助信息
-V            --version             显示 cl-launch 的版本和配置
-u FILE       --update FILE         将一个 cl-launch 脚本升级到当前版本

软件规范

-w CODE      --wrap CODE            shell wrapper CODE to run in cl-launch
-l LISP...   --lisp LISP...         尝试使用指定的LISP实现
-m IMAGE     --image IMAGE          从指定的映像构建
-f FILE      --file FILE            构建进包含指定的文件
-L FILE      --load FILE            构建是载入指定的文件
-S X         --source-registry X    覆盖 asdf 的系统源注册目录
-s SYSTEM    --system SYSTEM        构建时载入指定的 asdf 系统
             --load-system SYSTEM   和上面相同,为了与 buildapp 兼容
-p PACKAGE   --package PACKAGE      将当前包切换为指定的包
-sp SP       --system-package SP    相当于同进使用 -s 和 -p
-e FORM      --eval FORM            构建时求值指定的表达式
             --require MODULE       构建时 require 指定的模块
-r FUN       --restart FUN          通过调用 fun 从构建重新启动
-E fun       --entry FUN            通过调用 (fun argv) 从构建重新启动
-DE N/F      --dispatched-entry N/F 如果以 N 的名字执行,以 (F argv) 重新启动
-i FORM      --init FORM            重启后求值 FORM
-ip FORM     --print FORM           evaluate and princ FORM after restart
-iw FORM     --write FORM           evaluate and write FORM after restart
-F FORM      --final FORM           evaluate FORM before dumping IMAGE
-I PATH      --include PATH         cl-launch 安装位置的运行时路径
+I           --no-include           disable cl-launch installation feature
-R           --rc                   尝试读取 /etc/cl-launchrc, ~/.cl-launchrc
+R           --no-rc                跳过 /etc/cl-launchrc, ~/.cl-launchrc
-Q           --quicklisp            使用 quicklisp
+Q           --no-quicklisp         为使用 quicklisp
-b           --clbuild              使用 clbuild (see --more-help)
+b           --no-clbuild           不使用 clbuild
-v           --verbose              啰嗦模式
-q           --quiet                安静模式(默认)

输出选项

-x -o !      --execute              立即运行指定的软件(这是默认的行为)
-o FILE      --output FILE          创建可执行文件
-d IMAGE     --dump IMAGE           转储堆映像以加快启动
-X ... --                           使用 #!/.../cl-launch 作为脚本解释器
--           --                     当使用 -x 或 -X 时标志参数的结束

调用 cl-launch

cl-launch 将求值 Common Lisp 代码或创建求值 Common Lisp 代码的 shell 脚本或可执行二进制文件。 cl-launch 遵循 Unix 脚本解释器和 Common Lisp 实现的调用约定。

cl-launch 的建议缩写名称是 cl(如果它没有包含在操作系统的 cl-launch 包中,您可以自己创建符号链接)。 我们希望尽可能统一地使用路径/usr/bin/cl,以便脚本作者可以合理地期望脚本在以下情况下可以工作:

#!/usr/bin/cl

(请参阅下面的 简单的 cl-launch 脚本 以了解 #! 脚本的注意事项。)

最近的 Linux 内核对脚本解释器的支持本身就是一个脚本。 BSD 内核和 Linux 不同,BSD 需要将一个小的 C 程序 cl-shim 编译并安装到 /usr/bin/cl 才能以这种方式使用 cl-launch

要正常工作,cl-launch 4.1.3 依赖于 ASDF 3.1.2 或更高版本,以及它的可移植层 UIOP,来管理编译和映像的生命周期。

软件被指定为在几个阶段对代码进行求值;这种区别对于创建可执行二进制文件最为重要,但理解求值模型也可以避免在其他情况下出现意外。

第一阶段,初始化 Lisp 映像:

  • 可以选择从某个映像 IMAGE 开始 (-I --image 选项)[注:--image 的短选项不是 -m ?]

  • 加载一个小的代码头,提供常用的cl-launch功能

  • 载入 asdf 3

    cl-launch头将尝试加载“ASDF 3.1.2”或更高版本。如果您的实现没有通过(require "asdf")提供它的话,您可以配置实现的asdf(如果有的话)来找到它。或者你可以把它放在$HOME/common-lisp/asdfcl-launch会找到它。
    或者可以把它安装在/usr/share/common-lsp/source/cl-asdf/里,cl-launch也可以找到它。
    如果上述任何一项失败,cl-launch将无法继续。

  • (可选)加载quicklisp (-Q --quicklisp 选项)

第二阶段中,将根据以下选项,按照顺序构建软件:

  • 在当前包环境中求值一个或多个表达式 (-e --eval选项)。
    *package*已被设置为当前包的上下文中,该系列表达式的计算方式为LOAD(加载)(请参见下面关于包的解释)。

  • -L --load FILE 编译FILE并加载 fasl
    加载文件时,将*package*绑定到当前包(见下文)。

  • -f --file FILE include 一个文件,输出脚本中将包含的FILE的内容,然后被编译成 fasl 并加载,就像通过选项-L --load一样。与立即执行代码或转储映像相比,在创建输出脚本时,差异最为重要。这种方式只能指定一个文件。如果指定的文件名是-,则使用标准输入。因此,您可以连接多个文件,并通过管道将它们提供给cl-launch。如果要 include 的文件刚好叫做-,请传递参数./-(与cat和其他Unix命令的技巧相同)。

  • -X SCRIPT -- 执行脚本SCRIPT,或者 #! 脚本
    或者通过使用不以(-开头的文件名来执行选项,相当于:--package cl-user --load SCRIPT --execute --一样。

  • --require require 由实现提供的模块

  • 让 ASDF 3 编译并加载一个系统 (-s --system --load-system选项)
    选项-sp --system-package载入一个系统,然后像 -p --package一样切换当前包。

  • (可选) -d --dump 转储一个映像
    在求值一个或多个最终表达式之前 (-F --final选项)

如果使用选项-o --output创建一个 shell 脚本,但不使用选项-d --dump,那么前两个阶段仅在调用脚本时发生。如果使用选项-d --dump,那么这两个阶段会立即发生,并且在调用时不会再编译。请注意,已编译的文件是缓存的,因此只有在通过--load of --system首次加载文件时,或者在源文件被修改时,才会进行编译。这可能会导致首次启动速度变慢。缓存由“ASDF”的output-translations机制控制。有关此缓存的配置,请参阅“ASDF”手册,该手册通常位于~/.cache/common-lisp/下面。

第三阶段

您的软件通过UIOP:RESTORE-IMAGE运行。如果在脚本上使用选项-x --execute或通过#!cl-launch作为 Unix 解释器调用,第三阶段会立即发生;

如果您使用选项-o --output输出一个脚本或转储的映像(结合-d --dump选项),则第三阶段发生在对输出文件的调用时:

  • UIOP:*IMAGE-RESTORE-HOOK* 队列,按照 FIFO 顺序调用。

  • -i --init-ip --print-iw --write 指定的一系列“FORM”,以文本字符串的表达式存储,并按顺序进行读取和求值,每个表达式都在请求时当前的包上下文中求值。(这些表达式与分隔的空白连接在一起,构成了由RESTORE-IMAGE处理的UIOP:*IMAGE-PRELUDE*
    以左括号开头的参数被假定为隐式--print后面的“FORMS”。
    从流加载意味着你不必担心糟糕的读取时间问题;表达式将由完整构建的 Lisp 映像读取;然而,这也意味着,如果在调用转储映像时非常关心启动延迟的最后一滴,那么只需使用选项-r --restart-E --entry,并避免使用-init及其变体。
    选项-ip --print指定'FORMS',这样最后一个表达式的结果将像PRINC一样打印,后面跟着一个换行符。选项-iw --write类似于--print,使用write而不是PRINC

  • (可选) -r --restart-E --entry 指定的函数被调用。
    如果该函数通过选项 -r --restart指定(与早期版本的 cl-launch 兼容),它将不带参数调用。
    如果该函数通过选项 -E --entry指定(与 buildapp 兼容),它将使用一个参数调用,即传递给程序的参数列表,不包括 argv[0]在内。在大多数实现中可以通过函数 uiop:argv0来实现(在 ASDF 3.1.2 及更高版本中可用)。
    参数必须是能够从当前包中读取的函数名或 lambda 表达式(请参阅下面的选项 -p --package-sp --system-package)。
    只能指定一个restartentry函数,如果指定了多个,则最后指定的那一个将覆盖之前的。 如果你想调用多个函数,你可以使用DEFUN定义一个函数作为入口,再通过它来调用其它函数,或者你可以使用多个初始化表达式,如下所示。 另请参阅下面的选项 -DE --dispatch-entry-sm --system-main-Ds --dispatch-system,它们的行为就如同指定了 -E --entry 一样。

  • 如果未能提供restartentry函数,程序将以状态码0退出。如果提供了函数,程序将在函数返回后退出,当且仅当返回值是广义的布尔值“真”时,退出状态为0,如果返回值为NIL,则退出码为1。详细信息请参数"UIOP:RESTORE-IMAGE"文档。

当前包可以通过选项 -p --package 及其变体 -sp --system-package 来控制,它的行为也类似于 -s --system

传递给--eval--init--print--write--final--restart-entry 的所有表达式都会在当前包中读取。在当前包中读取用 -f--file--load 指定的文件。”当前“”指的是正在处理的选项之前的最新选项-p --package-sp --system-package指定的包,如果没有指定,则为cl-user

请注意,多个 -i --init-F --final 表达式可能会在包被更改后连续求值,并且如果这些表达式之一本身修改了包,或者其他一些语法控制机制,例如 reader,它可能会对同一类别的后续表达式产生不利影响,但不会影响其他类别的表达式(如果达到)。

以下衍生选项的工作原理类似于简单选项的组合:

  • 选项-sp --system-package--system--package组合在一个选项中,因此给定参数SYSTEM,系统就像通过--system SYSTEM加载一样,后者创建一个包system,然后该包成为当前包。

  • 如果使用选项 -DE --dispatch-entry,则下一个参数必须遵循格式 NAME/ENTRY,其中 NAME 是可以调用程序的名称(basename uiop:argv0),而 ENTRY 是一个函数,在这种情况下,就像通过 --entry 调用一样。 如果 ENTRY 被省略,则使用当前包中的函数 main
    对选项 -DE --dispatch-entry 的支持被委托给一个dispatch库,与 cl-launch 一起分发,但不是 cl-launch 本身的一部分,由

    1. 注册对dispatch库的依赖,就像通过--system cl-launch/dispatch(如果还没有注册的话)
    2. 如果 --restart--entry 都没有指定,就注册一个默认的入口函数。相当于
      --entry cl-launch/dispatch:dispatch-entry
    3. 注册一个构建表达式,该表达式注册调度条目。相当于
      --eval '(cl-launch/dispatch:register-name/entry "NAME/ENTRY" :PACKAGE)',其中PACKAGE是当前包。

有关更多详细信息,请参阅dispatch库的文档。

  • 如果选项 -Ds --dispatch-systemSYSTEM 作为其参数一起使用,相当于
    < -s | --system> SYSTEM < -DE | --dispatch-entry> sys-name/main
    其中,sys-name是系统的 basename, 路径中最后一个反斜杠后面的部分。

  • 如果选项 -sm --system-mainSYSTEM 作为其参数一起使用,就好像选项 -s --system 与相同的参数一起使用,然后是选项 -E --entry SYSTEM包中的main函数,但不改变当前包。

cl-launch 调用的一般说明:

选项从左到右处理; 通常,重复的选项会累积其效果,较早的实例在后面的实例之前生效。 在冲突或冗余选项的情况下,后者覆盖前者。

cl-launch 定义了一个导出以下符号的包 cl-launch

compile-and-load-file

以前由cl-launch提供的运行时功能现在由“UIOP”提供,该可移植层由“ASDF3”提供。请参阅下面的部分 cl-launch 运行时 API

当第一个无法识别的选项是文件名时,cl-launch 会尝试将此文件名作为脚本加载,就像通过 --load 一样,然后立即执行它,就像通过 --execute -- 一样, 命令行的其余部分作为参数传递。 文件名不能以字符 -( 开头 --- 要使用其中一个(或未知的东西)作为文件文件名的第一个字符,请在文件名前加上 ./。注意它 让攻击者控制传递给 cl-launch 或其他命令的文件名是一种安全风险。

当指定选项 --execute 时,执行指定的软件。 命令行参数可以通过将它们放在特殊标记 -- 之后来提供给正在执行的软件,这会结束 cl-launch 选项处理。

当使用选项 --output FILE 时,代码将生成到指定的 FILE 中。 输出文件本身将从完整生成的内容自动创建,因此可能与输入文件具有相同的路径名。不会求值restart函数和init表达式,但会在执行输出文件时保留。
如果指定了-(引用后),则使用标准输出。
如果指定了!(引用后),则假定选项--execute

当没有指定 --output 文件时,隐式假定选项 --execute。 最后一个 --output--execute 选项优先于之前的选项。

如果只存在一个参数并且它不以 - 开头,则该参数被视为提供给选项 -ip,将立即进行求值和打印。

可以使用选项 --source-registry SOURCE_REGISTRY 覆盖ASDF3 源注册配置。 提供的配置将优先于环境或配置文件提供的任何内容,尽管它可以像往常一样从它们继承。 请参阅 ASDF3 手册。

选项 -l --lisp-w --wrap 可用于控制在软件运行时找到 Common Lisp 实现的方式。 选项 -l --lisp 指定尝试使用的实现列表; 该列表以空格分隔,并包含 cl-launch 能够识别的昵称。

选项 -w --wrap 提供任意代码以供 shell 包装器求值,在它读取其配置并定义其内部函数之后,但在它尝试查找和运行 Lisp 实现之前。 此类包装器代码通常用于修改控制生成脚本的运行时行为的变量,如下所述。 可以使用 cl-launch 的其他内部结构,但不支持,这意味着您有责任保留与您的代码一起使用的特定版本的 cl-launch 的副本,并在您以后制作时更新您的代码 升级到不兼容的cl-launch。 例如,--lisp "foo bar" 等价于 --wrap 'LIPS="foo bar"'。请参阅下面有关 Lisp 实现调用 的文档部分。

选项 --no-include 指定 cl-launch 应该生成一个独立脚本,其中包括配置、shell 包装器、Lisp 标头和用户提供的 Lisp 代码(来自 --file)。 除了 ASDF 和 Lisp 实现,该脚本实际上是自包含的,可以在文件系统中随意移动后仍然可以继续使用。 然而,输出的大小将是用户 Lisp 代码的大小加上大约 36KiB。

选项 --include PATH 指定 cl-launch 应该生成一个非常小的脚本(通常在 1KiB 以下),运行时将从指定的安装目录 PATH 读取 cl-launch shell 包装器和 Lisp 标头。 此外,如果使用了选项 --include,并且 Lisp 代码是用 --file 和以 / 开头的绝对路径名而不是相对路径名或标准输入指定的,那么 Lisp 代码也将是 在运行时从指定位置加载,而不是在生成时嵌入到脚本中。 此选项生成更精简的脚本,但可能不适用于同一脚本用于缺乏通用一致文件系统管理的各种情况。

--include--no-include 中的哪一个是默认值可能取决于您的 cl-launch 安装。 作者分发的 cl-launch 版本默认使用 --no-include,但是您的操作系统分发中可用的 cl-launch 版本可能依赖于管理良好的包含路径(这是 以 debian 为例)。 您可以使用选项 --version 查询 cl-launch 实例的配置。

例如,人们可能期望使用 cl-launch 的 debian 版本:

/usr/share/common-lisp/source/cl-launch/

作为系统管理的包含路径。 人们还可能期望系统管理的 Lisp 实现会附带在 Lisp 图像中预编译的 cl-launch。 由于 cl-launch 提供了 :cl-launch 功能,并且由于 cl-launch Lisp 标头被条件化为不能使用此功能读取,这将使 cl-launch 启动更快,同时仍然允许非 -system-managed Lisp 实现运行良好。

您可以使用以下命令创建 cl-launch 安装:

    cl-launch --include /usr/share/common-lisp/source/cl-launch \
            --lisp 'sbcl ccl clisp' \
            --rc \
            --output /usr/bin/cl-launch -B install

如果您只想配置 cl-launch(例如,使用不同的默认值 --lisp 但没有 --include),则可以使用命令 -B install_bin,如果您想配置,则可以使用命令 -B install_path 只想创建支持文件。 请注意,--backdoor 选项 -B 必须在您的调用中排在最后。

选项 +R --no-rc 指定 cl-launch 不应尝试读取资源文件 /etc/cl-launchrc~/.cl-launchrc

选项 -R --rc 指定 cl-launch 应该尝试读取资源文件 /etc/cl-launchrc~/.cl-launchrc。 这些文件对于根据 $SOFTWARE_SYSTEM 定义覆盖 $LISP 的值特别有用。 提供了一个 shell 函数 system_preferred_lisps,以便您的 cl-launchrc 可能包含如下行:

    system_preferred_lisps stumpwm cmucl sbcl clisp
    system_preferred_lisps exscribe clisp cmucl sbcl

请注意,为了解析选项 --no-rc,资源文件选项被处理后运行,并且任何内部变量的覆盖将因此抢占用户指定的选项。 发生此类覆盖时,将在标准错误输出上打印警告。 请注意,此类覆盖仅在脚本创建时发生。 由 cl-launch 创建的脚本不会尝试读取 cl-launch 资源文件。

选项 +Q --no-quicklisp 指定 cl-launch 不应使用 quicklisp。 选项 -Q --quicklisp 指定 cl-launch 应该使用 quicklisp。 哪个是默认值取决于您的安装。 默认默认是+Q。 Quicklisp 从 ~/quicklisp/setup.lisp 加载(如果可用),否则从 ~/.quicklisp/setup.lisp 加载。

选项 -b --clbuild 指定 cl-launch 应该依赖 clbuild 来查找和调用 Common Lisp 实现。 选项 +b --no-clbuild 指定 cl-launch 不应依赖 clbuild 来查找和调用 Common Lisp 实现。 哪个是默认值取决于您的安装。 默认默认值是+b

cl-launch 生成的文件由几个易于识别的部分组成。 因此,这些部分可以被视为不同的软件,每个都可以在其自己的知识产权制度下使用(如果有的话)。 万一发生事故,您仍然可以通过剥离包装器来检索选项--file提供的确切原始代码,由明确标识的标记分隔。 搜索标记字符串"BEGINS HERE:"。 之后的一切都不是 cl-launch。 这可以通过后门选项 -B extract_lisp_content 自动完成。 cl-launch 在嵌入使用选项 --file 指定的文件时隐式使用此功能,以便您可以处理先前由 cl-launch 生成的脚本并更改它包装嵌入式 Lisp 代码的选项 进入可运行的软件。

作为替代方案,您还可以升级先前生成的脚本以使用当前版本的 cl-launch,同时使用选项 --update 保留其原始包装选项。 在这种情况下,软件规范选项将被忽略。 输出选项仍然适用。 指定-(引用后)作为要更新的文件意味着从标准输入中读取要读取的内容。 此功能可能不适用于由非常早期版本的 cl-launch 实用程序生成的脚本。 它应该适用于 1.47 之后的版本。

支持的 Lisp 实现

当前 cl-launch 支持的 Lisp 实现包括:

abcl allegro ccl clisp cmucl ecl gcl lispworks sbcl scl xcl

还有几个别名:

clozurecl gclcvs lisp openmcl

分别代表了ccl,gcl,cmucl,ccl

完全支持,包括生成独立可执行文件的实现:

sbcl:   1.2.2
clisp:  2.49
ecl:    13.5.1
cmucl:  20D
ccl:    1.10
lispworks:  Professional 6.1.0

完全支持,但是不支持生成独立可执行程序的实现:

gcl:     GCL 2.7.0 ansi mode (需要 checkout 最新的 git repo)
allegro: Allegro 9.0 (也曾与 5 一起使用)
scl:     Scieneer CL 1.3.9

不完全支持:

abcl:    ABCL 1.3.1 (不支持 dump image, 但是可以使用 abcl-jar)
xcl:     XCL 0.0.0.291 (无法 dump image)

GCL 仅在 ANSI 模式下受支持。 cl-launch 确实导出了 GCL_ANSI=t,希望 gcl 包装脚本能像在 Debian 中那样做正确的事情。 此外,ASDF 3 需要最新的GCL 2.7。 请注意,GCL 似乎不再被积极维护。

CLISP 上的独立可执行文件存在一些问题。 请参阅下面有关 独立可执行文件 的部分。

LispWorks 需要专业版; 不支持个人版,因为它不允许您控制命令行或转储映像。 转储的映像将打印横幅,除非您转储独立的可执行文件。 要转储映像,请确保您的目标目录中有许可证文件和/或 .../lispworks/lib/6-1-0-0/config/lwlicense (或使用反弹 shell 脚本 exec /path/to/lispworks "$@"),创建一个构建脚本:

echo '(hcl:save-image "lispworks-console" :environment nil)' > si.lisp
lispworks-6-1-0-x86-linux -siteinit - -init - -build si.lisp

LispWorks 还要求您拥有 ASDF 3.1.2 或更高版本; 确保已在源注册中安装和配置它。 LispWorks 的纯控制台变体没有标准名称; 旧版本的 cl-launch 假定默认的 lispworks; 从 4.1.2.1 开始,改为使用 lispworks-console,以避免冲突。 您可以使用 shell 变量 $LISPWORKS 来控制使用的名称,或者您可以将 lispworks-console 留在路径中,并使用符号链接、副本、shell 别名或简单的包装脚本来启用您最喜欢的较短名称 lispworkslwlwconlw-console等。

同样,可以按如下方式创建 allegro 的 mlisp 映像:

    alisp -e '(progn
               (build-lisp-image "sys:mlisp.dxl"
                :case-mode :case-sensitive-lower
                :include-ide nil :restart-app-function nil)
               (when (probe-file "sys:mlisp") (delete-file "sys:mlisp"))
               (sys:copy-file "sys:alisp" "sys:mlisp"))'

此外,cl-launch 支持使用 clbuild 作为包装器来调用 Lisp 实现,并带有 --clbuild 选项。

支持的 shell

cl-launch 已经使用 posh 0.47, bash 2.05, bash 3.1, zsh 4.3.2, dash 0.5.3 和 busybox 1.01 ash 测试通过。

调用 Lisp 实现

当调用 cl-launch 生成的脚本时,cl-launch shell 包装器将尝试使用它在给定列表中找到的第一个 Common Lisp 实现来执行 Lisp 代码,可以通过选项 --lisp 指定 cl-launch shell 包装器的运行时行为可以通过一系列环境变量进行配置。 这些变量可以由用户通过将它们导出到他的环境中来控制,或者可以在脚本生成时使用 cl-launch 选项 --wrap 来限制它们。

如果定义了变量“LISP”,shell 包装器将首先尝试由变量“LISP”命名的实现。 如果失败,它将尝试在脚本生成时提供的实现列表。 如果指定,生成的实现列表将作为选项 --lisp 的参数。 否则,cl-launch 将提供其默认值。 cl-launch 的当前实例的默认值是:

sbcl ccl clisp abcl allegro lispworks scl cmucl ecl mkcl gcl xcl

此“LISP”选择仅在系统准备时发生。 如果您转储图像,则脚本将始终使用转储图像的 Lisp 实现。 如果你不这样做,那么用户可能会覆盖实现。

请注意,这些是 cl-launch shell 包装器中内置的昵称,不一定是实际二进制文件的名称。 您可以控制实现昵称到实际二进制路径名的映射,以使用环境变量调用。 对于给定的实现昵称,环境变量将是给定昵称的大写。

因此,变量“$SBCL”控制在哪里寻找“sbcl”实现,而变量“$CMUCL”控制在哪里寻找“cmucl”实现。 如果找到具有匹配路径名的二进制文件(根据需要使用标准的 unix $PATH),则将使用所述实现,使用适当的命令行选项,可以使用类似于之前的环境变量但使用 _OPTIONS 附加到其名称。

因此,$CMUCL_OPTIONS 用于 cmucl$CLISP_OPTIONS 用于 clisp 等。为每个实现提供了合理的默认值,以便在非交互模式下执行软件,禁用调试器,无需读取用户- 具体的配置文件等

如果您想坚持使用带有给定选项的给定实现,您可以使用选项 --lisp--wrap,如下所示:

--lisp 'sbcl clisp' --wrap '
    LISP= # do not allow the user to specify his implementation
    SBCL=/usr/bin/sbcl # not any experimental thing by the user
    SBCL_OPTIONS="--noinform --sysinit /dev/null --userinit /dev/null \
    --disable-debugger" # predictable Lisp state
    CLISP=/usr/bin/clisp # fall back on machines that lack SBCL
    CLISP_OPTIONS=" -norc --quiet --quiet"
    # configure ASDF:
    CL_SOURCE_REGISTRY=/usr/local/share/common-lisp/source//:
    # assuming precompiled fasls there:
    ASDF_OUTPUT_TRANSLATIONS=/my/cl/src:/my/fasl/cache:

如果您转储图像,则无需取消设置“LISP”变量,但您可能仍希望覆盖用户指定的任何用户指定的“SBCL”和“SBCL_OPTIONS”(或您选择的实现的相应变量)。

请注意,您可以使用选项 --wrap "$(cat your_script)" 将文件中的完整脚本嵌入到您的程序中。 您的脚本可能会在运行 shell 包装器之前进行任意计算。 它可能会在运行 Lisp 之前进行一些一致性检查并中止。 或者它可以分析调用参数并对 Lisp 实现选项进行相应的调整。 这对于设置无法从 Lisp 代码设置的选项很有用,例如运行时映像的路径、交互式或非交互式执行、堆大小、源文件编码的区域设置等。

阅读 cl-launch 的源代码可能会非常疯狂。 您可能会非常有趣地了解事物为何如此,并在不破坏任何东西的情况下添加功能! 但是,添加对新 CL 实现的支持应该很简单:只需搜索 clispsbcl 的源代码并模仿我为它们所做的事情。 一定要把你最喜欢的本月 Lisp 风格发给我。

有限的 clbuild 支持

cl-launch 2.12 和更高版本支持使用 clbuild 作为包装器来配置您的 Lisp 实现,带有选项 --clbuild (如果它在默认情况下启用,则可以使用选项 --no-clbuild 禁用它 您的 cl-launch 安装)。

请注意,当您使用 clbuild 时,您不能再使用 SBCL_OPTIONS 来覆盖实现选项,因为 clbuild 会为您处理选项。 除非您指示 clbuild 这样做,否则不会删除任何实现横幅。 此外,您不能将 clbuild 与不同于 clbuild 的非可执行图像一起使用,这会排除使用 cmuclallegro 进行图像转储(allegro 可能会更新,但我没有最近的许可证 测试和开发)。

clbuild 支持目前尚未完全测试。 请报告任何错误。

简单的 cl-launch 脚本

在简单的情况下,您可以使用 cl-launch 创建一个 Common Lisp shell 脚本,而无需脚本生成步骤,因为您将花费大量时间编辑和分发它,而等待脚本启动时间的时间却很少 . 如果您没有在给定计算机上生成许多相同版本的脚本实例,那么这尤其是一个好主意。 如果这是您想要的,您可以使用 cl-launch 作为脚本解释以下方式(去除前导空格):

#!/path/to/cl-launch ...options...

例如,您可以编写以下脚本(去除前导空格):

#!/usr/bin/cl --entry main
(defun main (argv)
  (format t "Hello, World!~%~S~%" argv))

在最近的 Linux 内核上,选项可能包括空格、括号等,前提是它们在 shell 脚本中被引用。 此外,使用 -X 作为你的第一个选项和 -- 作为你的最后一个选项将确保脚本工作,即使它的名称以 (- 开头,除了使用旧版本的 cl-launch

但是请注意,Darwin (MacOS X) 和其他 BSD 内核或旧的 Linux 内核不喜欢 #! 解释器本身被解释。 在这些操作系统内核上,系统管理员必须编译和安装一个用 C 编写的小 shim,cl-shim.c,它将处理正确的脚本调用。

大多数内核对如何处理 #! 脚本的参数都有限制,这会阻止例如 使用 /usr/bin/env 作为蹦床; 但是,您可以使用以下完全可移植的解决方案,其中 ":" ; 确保脚本应保持有效的双语 shell 和 Lisp 代码:

#!/bin/sh
":" ; exec cl-launch -X -sp my-package -E main -- "$0" ${1+"$@"} || exit

(实际上用 "$@" 而不是 ${1+"$@"} 应该可以正常工作,除非您有古董 shell。)

请注意,如果您不需要从脚本中加载 Lisp 代码,并且一切都发生在构建规范中,那么您可以改用简单的 #!/bin/sh shell 脚本,您可以从中:

exec /path/to/cl-launch -x ... -- "$@".

此外,如果您不能依赖 cl-launch 位于固定路径,或者如果您的 shell 和/或内核组合不支持使用 cl-launch 作为脚本解释器,那么您可以改为启动 您的脚本包含以下几行:

#!/bin/sh
":" ; exec cl-launch -X -- "$0" "$@" || exit
(format t "It works!~%")

请注意,自 2.6.27.9 起,主线 Linux 内核仅支持 #!/usr/bin/cl-launch 中隐含的递归 #!

转储映像

您可以使用选项 --dump IMAGE 转储图像(用于静态编译和快速启动),其中 IMAGE 指定转储图像的路径。

如果您使用选项 --include PATH,则图像将从该指定目录而不是您转储它的目录加载回来。 如果您正在准备将脚本安装在另一个地方(可能是在另一台计算机上),这很有用。

cl-launch 提供的所有 CL 实现当前都支持此选项。

作为限制,LispWorks 将在标准输出上打印横幅,除非您使用下面的独立可执行选项。

作为另一个限制,当从先前转储的图像(使用 --image)运行时,ECL 将无法转储图像。 这是因为 ECL 的链接模型,您需要能够定位在链接原始图像时使用了哪些目标文件,跟踪这些文件,并将它们的列表添加到链接到的目标文件中 垃圾场。 这在概念上并非不可能,并且欢迎使用补丁。 但是,我们希望有一天能够通过一个真正的构建系统来支持这一点,例如 XCVB。

独立可执行程序

您可以使用选项 --dump '!' 创建独立的可执行文件(或通过提供与 --output 参数相同的 --dump 参数)。

目前只有 SBCLECLCLISPCMUCLCCLLispWorks Professional 支持此选项。 此外,CLISP 有以下问题。

如果使用诸如 --clisp-help--clisp-x '(sys::main-loop)' 之类的选项调用,CLISP 独立可执行文件会做出神奇的反应。 误打误撞是一件牵强附会的事情,“CLISP”维护者认为它是一个特性(我不这么认为)。 不要使用诸如 setuid 之类的可执行文件,也不要让不受信任的用户控制赋予这些以额外权限运行的可执行文件的参数。

cl-launch 运行时 API

cl-launch 提供了以下 Lisp 函数:

函数 cl-launch:compile-and-load-file 将源路径名指示符和关键字参数 force-recompile(默认为 NIL)和 verbose(默认为 NIL)作为参数。 如果明确要求,或者文件不存在,或者 fasl 不是最新的,它将安排编译指定的源文件。 它将以指定的详细程度进行编译和加载。 它将使用 uiop:compile-file-pathname* 来确定 fasl 路径名。

cl-launch 之前提供的以下变量和函数已替换为 ASDFUIOP

变量 cl-launch:*arguments* 被替换为 uiop:*command-line-arguments*

函数 cl-launch:getenv 被替换为 uiop:getenv

函数 cl-launch:load-system 被替换为 asdf:load-system

函数 cl-launch:quituiop:quit 替换(注意:lambda-list 略有不同)。

此外,环境变量 CL_LAUNCH_PIDCL_LAUNCH_FILE 将分别设置为进程 ID 和脚本调用文件名。

详细输出模式

如果shell变量CL_LAUNCH_VERBOSE被导出并且非nil,那么cl-launch和它生成的脚本会产生大量的输出,显示诸如Lisp调用命令,编译和加载文件用: 详细的 t:print t 等。这仅对调试 cl-launch 和/或您的构建过程有用。 选项 --verbose 设置此变量,而选项 --quiet 重置它。

Makefile 的例子

### Automatically download of the current version of cl-launch if not present
cl-launch.sh:
        wget -O cl-launch.sh http://fare.tunes.org/files/cl-launch/cl-launch.sh
        chmod a+x cl-launch.sh

### Making a shell script executable from a simple Lisp file named foo.lisp
foo.sh: cl-launch.sh foo.lisp
        ./cl-launch.sh --output foo.sh --file foo.lisp

### A more complex example using all options.
run-foo.sh: cl-launch.sh preamble.lisp
        ./cl-launch.sh --output run-foo.sh \
        --file preamble.lisp --system foo \
        --init "(foo:main uiop:*command-line-arguments*)" \
        --source-registry ${PREFIX}/cl-foo/systems: \
        --lisp "ccl sbcl" --wrap 'SBCL=/usr/local/bin/sbcl-no-unicode' \
        --no-include

### An example with horrible nested makefile, shell and Lisp quoting
hello:
        opera=wORlD ; ./cl-launch.sh --execute --init \
        "(format t \"~25R~A~A~%\" 6873049 #\\space '$$opera)"

给 Lispor 的警告

cl-launch 开始在 cl-user 包或您指定的任何包中求值您的 Lisp 软件。 在求值初始化表达式时,包可能已经更改,也可能没有更改,具体取决于“加载”的细粒度语义。 如果这些事情很重要,请务必使用in-package。 如果您更改 readtable,甚至可能会发生更奇怪的事情。

在编写 shell 命令时,由于引用不当,有很多方法会出错。 cl-launch 做了正确的事,但你仍然必须小心 make、shell 和 Lisp 的嵌套引用机制。

下面是一个使用 cl-launch 的简单示例,用于快速比较各种系统上相同计算的结果:

for l in sbcl cmucl clisp gcl ccl ; do
  ./cl-launch.sh --lisp $l --execute --init \
    '(format t "'$l' ~A~%" most-positive-fixnum)' ; done

在内部,cl-launch 包含许多自检功能。 例如,您可以尝试(从可能创建垃圾的目录中):

./cl-launch.sh -l 'sbcl cmucl clisp gclcvs' -B tests

分享和享受!

请参阅我们的网页:http://www.cliki.net/cl-launch

注意:如果此帮助对您来说太长,您可以向后滚动,或使用:

    cl --more-help | less
posted @ 2022-02-15 16:42  fmcdr  阅读(164)  评论(0编辑  收藏  举报