请在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 不加载某些文件,我不得不想出一个相当繁琐的解决方案,它试图设置刷新目录为类路径目录减去有问题的那些,但如果能够通过黑名单或谓词来设置该功能,那就更加方便。

如果其他人遇到,以下是一个解决方案

(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 dep引入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,你认为提供一个 implementing this 功能的补丁会被接受吗?

我在想添加基于predicate的路径过滤器到这个函数的末尾: 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

它不会 специфically 解决我的问题,而是说“在find the files in refresh-dirs中找到的所有文件中,不考虑这个predicte应用的文件”。
不,这对我来说仍然没有意义。
> 源文件不是天然就应该存在于源目录中以便加载的吗?

> :paths 整个意思就是 "需要添加到类路径上的源路径"。如果这些文件不是源文件,似乎它们不应该在那里。

我感觉我在这方面的理解中还存在着一些差距,这些问题都与您之前的评论有关;

如果我是一个库的作者,我想要将一些 .clj 文件与我库一起分发(它们必须是库的一部分,而不是一些辅助的东西),它们不应该被加载,那么当我的库以以下形式分发时,最好的方式是什么:a) 作为 jar 文件;b) 作为 git 依赖项?
所有 jar 文件(以及由 git 依赖项的 :paths 定义的类路径)都在类路径上,任何 jar 中的 clj 文件都可以通过 Clojure 加载。

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

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

...