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

上述示例失败是因为以下组合

由于目前无法通过工具命名空间告知不加载某些文件,我不得不采取一种较为漏洞性的解决方案,该解决方案尝试将刷新目录设置为类路径目录减去有问题的那些,但如果有办法通过黑名单或谓词(predicate)来设置这将更加理想。

解决方案 假设其他人也可能遇到同样的问题

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

当类路径上有未打算加载的clojure源文件时,这会引发问题。

这看起来像是你自己的问题构建出来的?源目录中的源文件不是天生要被加载的吗?你不能不那样做吗?

by
嗯,其实这并不是我自己的问题本身;我只是尝试在一个项目中刷新next.jdbc通过git依赖...这就是next.jdbc提供给clj-kondo的提示信息。

虽然我同意源文件夹中的clj文件的主要用例是进行加载,但我也可以想象其他场景,例如以这种方式分发示例代码文件等,这些文件本身可能无法在应用中加载。
by
编辑 by
但是为什么它们在: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中找到的所有文件中,不应考虑适用于此谓词的文件”的方法。
.
不,这对我来说仍然没有意义。
.
> 源代码文件不自然是存储在源目录中以便加载的吗?

> "路径列表"的整个意思是“包含要放入类路径中的源路径”。如果它们不是源代码,似乎就不应该在那里。

我觉得我的理解有缺陷,需要回到您之前评论的内容;

如果我是一个库的作者,并希望将一些 .clj 文件与我库一起分发(它们应该包含在库中,而不是辅助的内容),它们不应被加载,当我的库以以下哪种方式分发时,哪种方式最好:a) 一个 jar 文件 b) 一个 git 依赖项?
.
所有 jar 文件(以及由 git 依赖项的路径定义的类路径)都在类路径上,并且其中的任何 clj 文件都可通过 Clojure 加载。

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

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

...