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

欢迎!请访问关于页面获取更多关于如何使用本站的信息。

0
tools.namespace
编辑

简述
目前影响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 不要加载某些文件,我不得不想出了一个相当流氓的解决方案,该方案试图将刷新目录设置为类路径目录减去有问题的目录,但如果可以设置一个黑名单或谓词来进行设置,那就好多了。

解决方案 以供其他人遇到此问题参考

(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文件的主要情况是它们应该被加载,但我也可以想象其他场景,例如以这种方式分发示例代码文件等,这些文件可能不一定会被应用在app本身中加载。

编辑于
但为什么那些文件在 :paths 中呢?:paths 整个单词的意思是 "包含要添加到类路径上的源文件的路径"。如果这些文件不是源文件,那么它们似乎不应该在那里。项目可以创建别名来添加这些路径,如果某些特定的工具需要这些路径。
谢谢,这对我来说很有道理。我会联系相关工具的作者。

话虽如此,我还是喜欢在 deps.edn 中设置 :exclusions 的可能性,以防库 pulls 抽取我不需要/想要的依赖 - 有时这有助于保护我的工作流程/产品不受库作者犯的一些错误的影响。如果 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 的问题致力于从该方向解决这个问题。

...