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)

"要求是这些文件在clj-kondo.exports目录下的classpath中,这样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 库发布“源”和“javadoc”工件相同的机制)。如果您不希望它们作为 clj 源加载,也可以使用不同的文件扩展名(例如 .edn 或您想要的任何名称 - 您仍然可以选择通过读取器将它们加载为 Clojure,但它们不会被看作 Clojure 命名空间)。
感谢您的回答,Alex。
0 投票

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

...