如何在aot编译之前设置动态var



我希望在给定的命名空间中有一个*flag*变量,该变量仅在:aot编译时设置为true

有办法做到这一点吗?

您的问题有点复杂,因为Clojure自己的动态特性的定义,所以在编译时没有C的#ifdef或其他机制的粗略等价物,但这里有一个解决方法:

我用lein new app flagdemo创建了一个Leiningen项目。这个技巧可以检测何时执行AOT,如上面提到的@Biped Phill,使用动态变量*compile-files*,并在编译代码的类路径中保存一个资源:

代码如下:

(ns flagdemo.core
(:gen-class))
(def flag-path "target/uberjar/classes/flag.edn")
(defn write-flag [val]
(try (spit flag-path (str val)) (catch Exception _)))
(defn read-flag []
(some-> (clojure.java.io/resource "flag.edn") slurp clojure.edn/read-string))
(write-flag false)
(when clojure.core/*compile-files*
(write-flag true))
(defn -main
[& args]
(println "Flag is" (read-flag)))

因此,当文件使用lein run加载时,或者当您在REPL中加载它时,它将尝试写入值为false的EDN文件。

当您使用lein uberjar编译包时,它加载名称空间并发现*compile-files*已定义,因此它将与JAR一起打包的EDN文件保存为资源。

函数read-flag只是尝试从类路径加载EDN文件。

它是这样工作的:

$ lein clean
$ lein run
Flag is nil
$ lein uberjar
Compiling flagdemo.core
Created /tmp/flagdemo/target/uberjar/flagdemo-0.1.0-SNAPSHOT.jar
Created /tmp/flagdemo/target/uberjar/flagdemo-0.1.0-SNAPSHOT-standalone.jar
$ java -jar target/uberjar/flagdemo-0.1.0-SNAPSHOT-standalone.jar
Flag is true

感谢@Biped Phill和@Denis Fuenzalida向我解释了这一点。

我认为这也有效:

(def ^:dynamic *static* false)
(when (and *compile-files* boot/*static-flag*)
(alter-var-root #'*static* true))

则在profiles.clj:中

:injections [(require 'boot)
(intern 'boot '*static-flag* true)]

首先,您需要指示变量将随时间变化:

(def ^:dynamic *the-answer* nil)
*the-answer*
;=> nil

^:dynamic位将允许该变量稍后反弹:

(binding [*the-answer* 42] *the-answer*)
;=> 42

附录1

以下是如果您不使用^:dynamic:会发生的情况

(def *the-answer* nil)
(binding [*the-answer* 42] *the-answer*)
;=> [...] Can't dynamically bind non-dynamic var [...]

附录2

这类变量通常被称为"耳罩"。命名约定是不强制的,但强烈鼓励。

以下是当您在不声明动态变量的情况下使用此命名时REPL中发生的情况:

(def *the-answer* nil)
; Warning: *the-answer* not declared dynamic and thus is not dynamically rebindable, but its name suggests otherwise. Please either indicate ^:dynamic *the-answer* or change the name. (/tmp/form-init7760459636905875407.clj:1)

最新更新