Phoenix Umbrella项目编译顺序问题:宏未定义偶发报错解决方案咨询
这个问题我之前维护Umbrella项目时也碰到过,本质是编译阶段的依赖顺序没明确配置,导致child应用在main的宏还没编译完成时就尝试调用它,从而出现偶发的"未定义宏"错误。下面是几个可靠的解决方法:
1. 明确child应用对main的编译依赖
Umbrella应用默认不会自动处理内部应用的编译顺序,除非你明确声明依赖。打开apps/child/mix.exs,做这两个修改:
第一步:在依赖中添加main应用
在defp deps do块里,把main作为内部依赖加入:
defp deps do [ # 其他依赖... {:main, in_umbrella: true} ] end
in_umbrella: true告诉Mix这是同一个Umbrella下的应用,会自动处理路径和编译顺序。
第二步:在应用声明中包含main
找到def application do部分,把:main加入:applications列表(Elixir 1.10+也可以用:extra_applications,但内部应用更推荐用:applications明确声明):
def application do [ mod: {Child.Application, []}, applications: [:main, :logger] # 把:main加到这里 ] end
这样Mix会确保main应用完全编译完成后,才开始编译child应用。
2. 强制指定Umbrella的全局编译顺序
如果上面的配置还偶尔出现问题,可以在Umbrella根目录的mix.exs里,直接指定apps的编译顺序。找到def project do中的apps配置,把main放在最前面:
def project do [ apps_path: "apps", apps: [:main, :main_web, :child], # 强制先编译main # 其他配置... ] end
Mix会严格按照这个列表的顺序编译应用,彻底避免child先编译的情况。
3. 临时应急方案(不推荐长期使用)
如果你只是临时需要快速解决问题,可以手动先编译main再编译整个项目:
mix compile apps/main && mix compile
但这只是权宜之计,最好还是通过配置依赖让Mix自动处理,避免每次编译都要手动操作。
为什么会出现这个问题?
宏是在编译阶段执行的代码,调用宏的模块必须在宏定义模块已经编译完成之后才能编译。如果child应用没有明确声明对main的依赖,Mix可能会并行编译多个应用,或者按apps目录的字母顺序(比如child目录在main前面)先编译child,这时候child里调用Main.MyMacro的代码就会找不到已编译的宏定义,从而报错。再次编译时,main可能已经被缓存编译完成,所以错误消失,这也是问题偶发的原因。
内容的提问来源于stack exchange,提问作者Sam Houston

