我希望在给定的命名空间中有一个*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)