请参与2024 Clojure 状况调查!

欢迎!请查阅关于页面以获取更多关于如何使用本站的信息。

0
tools.namespace
编辑

简洁版
目前改变哪些源文件由 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 不要加载某些文件,我不得不想出一个相对hack的解决方案,该方案尝试将刷新目录设置为类路径目录减去有问题的目录,但如果能通过黑名单或谓词来设置这会更美好。

解决方案 供他人遇到时参考

(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依赖 pulls in 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,你认为提供一个具有此功能的补丁会被接受吗?

我考虑在以下函数末尾添加基于谓词的路径过滤: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 库发布“source”和“javadoc”工件相同的机制)。如果您不希望它们作为 clj 源文件的加载,您也可以使用不同的文件扩展名(例如 .edn 或您想要的任意扩展名 - 您仍然可以选择通过读取器将它们作为 Clojure 加载,但它们不会被看作 Clojure 命名空间)。
感谢您的回答,Alex。
0

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

...