请在 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 是一个静态分析器/linter。为了能够正确分析自定义宏,它允许库将描述这些宏应该如何被分析的字节码文件(clj)作为资源在特定目录下分发。

上述示例失败是因为以下原因的结合

因为没有方法可以告诉工具命名空间不加载某些文件,我不得不想出一个稍微噱头的解决方案,这尝试将刷新目录设置为类路径目录减去有问题的那些,但如果有一个方法可以通过拒绝列表或谓词来设置这个值会更好。

解决方案 以备他人遇到相同问题

(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

当类路径上有源目录中的Clojure源文件,而这些文件根本不应该被加载时,就会出现问题。

这看起来像是你自己的构造问题?源目录中的源文件难道不是天生就为了加载而存在的吗?你不能不做这件事吗?

好吧,说起来也不是我自己的专长;我只是试图在一个通过git依赖引入next.jdbc的项目中进行刷新...这就是next.jdbc向clj-kondo提供提示的方式。

虽然我同意源文件夹中clj文件的主要情况是为了加载,但我也可以想象其他场景,例如通过这种方式分发示例代码文件等,这些文件在应用本身中不一定能被加载。

编辑了
但为什么要在: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知道将哪些内容复制到本地配置目录”
Alex,你认为提供一个提供这种功能的补丁会被接受吗?

我打算在你指出这个功能的地方添加基于谓词的路径筛选:[链接](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

现在有一个clj-kondo 问题,尝试从这个方面解决这个问题。

...