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

欢迎!请查看关于页面以了解更多关于如何使用本站的信息。

0
tools.namespace
编辑

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

长话短说

我们在clojure.tools.namespace/refresh与clj-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文件的主要用途是加载,但我也可以想象其他场景,比如通过这种方式分发示例代码文件等,这些文件本身可能无法在应用程序中加载。

编辑
但是为什么它们在:paths里面?:paths的全称是“包含源文件的路径,需要放在类路径上”。如果这些文件不是源文件,似乎就不应该放在那里。项目可以在需要某些特定工具用途时创建别名来添加这些路径。
谢谢,这个解释对我来说很有意义。我会和这个工具的作者回电。

尽管如此,我还是很喜欢在deps.edn设置:exclusions的可能性,以防某个库拉进了我不需要/不想要的依赖 - 有时这有助于保护我的工作流程/产品免受库作者的某些错误影响。如果tools.namespace也有这个能力就太好了,但如果你认为这个情况目前不足以支持这个功能,我也能理解。
想知道“为什么它们在路径上”的答案,可以查看这个Clojurians Slack线程:[点击查看](https://clojurians.slack.com/archives/CHY97NXE2/p1641423463308400?thread_ts=1641398954.304500&cid=CHY97NXE2)

要求是这些文件在classpath下的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 的整个含义是 “需要放入 classpath 的包含源代码的路径”。看起来如果文件不是源代码,那么它们不应该在那里。

我的理解似乎存在一个断层,再次回到您之前的评论;

如果我是库的作者,并且想在库中分发一些 .clj 文件(它们必须是库的一部分,而不是辅助工具),但又不希望它们被加载,当我的库以以下形式分发时,我应该如何操作:a)jar 格式 b)git 依赖项?
所有 jar 文件(以及 git 依赖项中定义的 :paths classpath)都在 classpath 上,并且它们中的任何 clj 文件都可以由 Clojure 加载。

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

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

...