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文件(描述这些宏应该如何被分析)作为资源。

上面的示例因为以下原因失败

由于目前无法让工具命名空间停止加载某些文件,我不得不想出一个相当棘手的解决方案,它试图将刷新目录设置为类路径目录减去有问题的目录,但如果有办法能够使用阻止列表或谓词来设置这将非常好。

如何处理,以备其他人在遇到此问题时使用

(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投票

当classpath上存在不应加载的全部是clojure源文件时,这会引发问题。

这看起来像是你自己的构造问题吗?源目录中的源文件不应该是 inherent there to be loaded 吗?你不能不做那件事吗?

嗯,不是完全是我自己的;我只是在尝试更新一个通过git依赖项拉取next.jdbc的项目...这是next.jdbc提供给clj-kondo的提示方式。

虽然我同意source文件夹中的clj文件的主要用例是加载,但我也可以想象其他场景,比如以这种方式分发示例代码文件等,这些文件本身可能无法在本应用程序中加载。
但是为什么它们在:paths中?:paths的全局意思是"包含要添加到classpath上的源路径"。看起来如果这些源文件不是源文件,它们就不应该在其中。项目可以根据需要为一些特定工具目的创建路径别名。
谢谢,这对我来说很有意义。我将回复相关工具的作者。

话虽如此,我还是非常喜欢在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 dep?
所有 jar 文件(以及由 git dep 的 :paths 定义的类路径)都在类路径上,并且其中的任何 clj 文件都可以被 Clojure 加载。

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

现在有一个clj-kondo问题,尝试从那一侧解决此问题。

...