请在2024年Clojure状态调查!中分享您的想法。

欢迎!请查看关于页面以了解更多关于这个系统的工作方式。

0
tools.namespace
编辑

TL;DR
目前影响clojure.tools.namespace.repl/refresh加载哪些源文件的唯一方法是设置刷新目录,这是一个基于白名单的系统。无法阻止特定目录/文件/模式被加载,同时允许其他所有内容。这在类路径上有Clojure源文件但不应加载到任意情况下时,会引发问题。

长篇大论

我们遇到了clojure.tools.namespace/refreshclj-kondo钩子的一个有趣问题。

一个单行复制案例是

clj -Srepro -Sdeps '{:deps {org.clojure/tools.namespace {:mvn/version "1.2.0"} seancorfield/next.jdbc {:git/url "https://github.com/seancorfield/next-jdbc/" :git/sha "24bf1dbaa441d62461f980e9f880df5013f295dd"}}}' -M -e "((requiring-resolve 'clojure.tools.namespace.repl/refresh-all))"

这将失败

:error-while-loading hooks.com.github.seancorfield.next-jdbc
Could not locate hooks/com/github/seancorfield/next_jdbc__init.class, hooks/com/github/seancorfield/next_jdbc.clj or hooks/com/github/seancorfield/next_jdbc.cljc on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.

为了背景,clj-kondo是一个静态分析器/编码规范程序。为了能够正确分析自定义宏,它允许库在特定目录下分发clj文件(描述如何分析这些宏)作为资源。

上述示例失败是由于以下组合

由于目前没有方法告诉 tools.namespace 不要加载某些文件,我不得不想出一个稍微有点奇怪的解决方案,该方案试图将刷新目录设置为类路径目录减去有问题的一些目录,但如果存在一种可以通过黑名单或谓词设置此选项的方法那就更好了。

workaround( workaround)以防其他人遇到相同的问题

(defn remove-clj-kondo-exports-from-tools-ns-refresh-dirs
  "A potential issue from using this is that if the directory containing the clj-kondo.exports folder
  also directly contains to-be-reloaded clojure source files, those will no longer be reloaded."
  []
  (->> (clojure.java.classpath/classpath-directories)
       (mapcat
        (fn [^File classpath-directory]
          (let [children   (.listFiles classpath-directory)
                directory? #(.isDirectory ^File %)
                clj-kondo-exports?
                           #(= "clj-kondo.exports" (.getName ^File %))
                has-clj-kondo-exports
                           (some (every-pred clj-kondo-exports? directory?) children)]
            (if has-clj-kondo-exports
              (->> children
                   (filter directory?)
                   (remove clj-kondo-exports?))
              [classpath-directory]))))
       (apply clojure.tools.namespace.repl/set-refresh-dirs)))

;; call in user.clj
(remove-clj-kondo-exports-from-tools-ns-refresh-dirs)

2 个回答

0
by

当类路径上有源目录中的clojure源文件时,这些源文件并未打算加载,这将产生问题。

这似乎是你自己构造的问题?源目录里的源文件难道不是天然要被加载的吗?难道不能不加载这些文件吗?

by
好吧,这部分并非完全是我自己的;我只是在尝试刷新一个通过git依赖拉取next.jdbc的项目...这就是next.jdbc是如何为clj-kondo提供提示的。

虽然我同意源文件夹中clj文件的主要用途是加载,但我也可以想象其他场景,比如以这种方式分发示例代码文件等,这可能不会在应用程序本身中被加载。
by
编辑 by
但为什么它们在 :paths 中?:paths 整个的含义是“包含要放在类路径中的源代码的路径”。如果这些文件不是源代码,那么它们似乎不应该在那里。如果为了某些特定工具的目的需要,项目可以创建别名来添加这些路径。
谢谢,这对我来说很有意义。我将回头与问题的工具作者联系。

尽管如此,我很喜欢在 deps.edn 中设置 :exclusions 的可能性,以防库拉入了我不想或不需要的依赖项 - 这有时有助于保护我的工作流程/产品免受库作者犯的错误。如果 tools.namespace 也具备这种功能那就太好了,但如果你认为目前的案例还不够强大,我也能理解。
如果有人想知道为什么它们在路径中,这个 Clojurians slack 线上的答案是: https://clojurians.slack.com/archives/CHY97NXE2/p1641423463308400?thread_ts=1641398954.304500&cid=CHY97NXE2

“要求是这些文件必须在 clj-kondo.exports 目录下位于类路径中,这样 clj-kondo 就知道要将什么复制到本地配置目录”
亚历克斯,你认为提供这个功能的补丁会被接受吗?

我想到的是在函数的末尾添加基于谓词的路径过滤: https://github.com/clojure/tools.namespace/blob/c0b333e127e14c2ac6d5b04d14d0e714d08bfdbb/src/main/clojure/clojure/tools/namespace/dir.clj#L28

类似于 set-refresh-dirs 的工作方式: https://github.com/clojure/tools.namespace/blob/master/src/main/clojure/clojure/tools/namespace/repl.clj#L164

这不会针对我的具体问题,而是一种说明“在所有在 refresh-dirs 中找到的文件中,不考虑这些文件应用了这个谓词”的方式
不,这对我来说还是没有意义。
在源目录中,源文件难道不是本身就是要被加载的吗?

整个 :paths 的意义就是“需要添加到类路径的包含源文件的路径”。似乎如果文件不是源文件,它们就不应该在那里。

我的理解似乎存在一些空白,这些空白回到了你之前的评论;

如果我是库的作者,并且想将一些.clj文件与我的库一起分发(它们应该包括在库中,而不是辅助内容),我不希望它们被加载,那么当我的库以以下方式分发时,我应该如何做?a)以.jar文件的形式分发 b)以git依赖项分发?
所有jar文件(以及由git依赖项的:paths定义的类路径)都在类路径上,并且其中的clj文件都可以被Clojure加载。

如果你要分发其他内容,你可以在Maven中通过不同的分类器发布工件(不是“jar”,但是与Java库发布“source”和“javadoc”工件相同的机制)。如果你不希望它们被作为clj源文件加载,你也可以使用不同的文件扩展名(如.edn或你想要的任何其他扩展名 - 你仍然可以选择通过读取器将它们作为Clojure加载,但它们不会被视为Clojure命名空间)。
感谢您,Alex。
0
by

目前有一个clj-kondo问题旨在从该方面尝试解决这个问题。

...