Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

依赖循环有什么好办法 #3118

Closed
zeromake opened this issue Nov 12, 2022 · 15 comments
Closed

依赖循环有什么好办法 #3118

zeromake opened this issue Nov 12, 2022 · 15 comments
Labels

Comments

@zeromake
Copy link
Contributor

Xmake 版本

v2.7.2+master.52dd7982a

操作系统版本和架构

Windows 10 version 19043.2130

描述问题

现在有两个库用 freetypeharfbuzz 举例(类似于这个两个库的依赖关系)。

之前两个库是直接用 xmake.lua 的 target 的方式走静态链接,直接把两个库源码互相 include,由于静态库是不需要检查依赖的库的方法所以顺便先编译哪个都行。

后来把这个两个库转为 packages 就发现了会互相依赖,而且发现如果我在最上层声明了依赖 configs 依赖里的相同库还是用自己的 configs,如果直接在 packages 里去强制依赖会直接死循环。

除了依赖循环的问题所在,还有着依赖的 packages 里的依赖的 configs 被丢弃,无法通过最上层的 add_requires("", {configs={xxx=true}}) 来覆盖。

期待的结果

看了一下 xmake 里的代码 import("package.tools.xmake").install 是一个新进程,要么用环境变量要么用文件来同步信息了。

c/c++ 是没办法搞什么一个库引入两个版本的操作,是否可以考虑想办法压平依赖树的方式,然后子依赖编译时应当继承顶层的 configs 声明(也就是用同一份)。

不过这个只能解决依赖的 configs 问题,也就是类似 sdl_ttf 库依赖 freetype 但是 freetype 有大把依赖才能开启的特性。

现在没有太好解决方案,我先把两库放一个里编译吧。

工程配置

我专门做了一个这样的 repo

xmake repo -a local https://github.com/zeromake/cycle-xrepo

在任意有 xmake.lua 的项目下执行以下命令就会看到 freetype 依赖一个 harfbuzz 方法并不存在但是如果我在 harfbuzz 里强制依赖 freetype,就会达成无限循环。

xmake require -f -y -vD --build --extra="{configs={harfbuzz=true}}" freetype

附加信息和错误日志

No response

@zeromake zeromake added the bug label Nov 12, 2022
@zeromake
Copy link
Contributor Author

@waruqi

自己手动改了 xmake 去支持了,不过代码比较乱

commit

一、体验效果

# 更新到我的分支代码> xmake update -s git@github.com:zeromake/xmake.git#master

# 创建一个目录来测试> mkdir demo && cd demo

# 添加 xmake.lua 和 c 代码> cat>xmake.lua<<EOF
add_rules("mode.debug", "mode.release")

add_requires("freetype", {configs={harfbuzz=true}, lazy_options={configs={harfbuzz=false}}})
add_requires("harfbuzz", {configs={freetype=true}})

target("demo")
    add_files("main.c")
    add_packages("freetype", "harfbuzz")
EOF> cat>main.c<<EOF
#include <ft.h>

int main() {
    ft_font_create();
    return 0;
}
EOF

# 添加有依赖循环的 repo> xmake repo -a local https://github.com/zeromake/cycle-xrepo

# 然后安装 require> xmake require -f -y -vD --build
# 摘抄一些日志
xmake f --diagnosis --verbose --yes -y -c --use-harfbuzz=n --plat=macosx --arch=x86_64 --mode=release --kind=static --cxflags=-fPIC --buildir=build_b6eb879f
patching /Users/zero/.xmake/packages/f/freetype/2.12.1/b6eb879fe6b242249cca33632327b448/lib/pkgconfig/freetype.pc ..
  => install freetype 2.12.1 .. ok

xmake f --diagnosis --verbose --yes -y -c --use-freetype=y --plat=macosx --arch=x86_64 --mode=release --kind=static --cxflags=-fPIC --buildir=build_c297384b
patching /Users/zero/.xmake/packages/h/harfbuzz/5.3.1/c297384bf19f440e807dd60936cbcab1/lib/pkgconfig/harfbuzz.pc ..
  => install harfbuzz 5.3.1 .. ok

xmake f --diagnosis --verbose --yes -y -c --use-harfbuzz=y --plat=macosx --arch=x86_64 --mode=release --kind=static --cxflags=-fPIC --buildir=build_b49f1a35
patching /Users/zero/.xmake/packages/f/freetype/2.12.1/b49f1a356eae427ca7ad0ddf8af1b12b/lib/pkgconfig/freetype.pc ..
  => install freetype#1 2.12.1 .. ok

# 上面的 freetype 编译了两次一次 --use-harfbuzz=n 一次 --use-harfbuzz=y。

# 最后就正常 xmake build> xmake build -vD demo

[ 25%]: cache compiling.release main.c
"/usr/bin/xcrun -sdk macosx clang" -c -Qunused-arguments -target x86_64-apple-macos12.3 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk -fvisibility=hidden -O3 -isystem /Users/zero/.xmake/packages/f/freetype/2.12.1/b49f1a356eae427ca7ad0ddf8af1b12b/include -isystem /Users/zero/.xmake/packages/h/harfbuzz/5.3.1/c297384bf19f440e807dd60936cbcab1/include -DNDEBUG -o build/.objs/demo/macosx/x86_64/release/main.c.o main.c
[ 50%]: linking.release demo
"/usr/bin/xcrun -sdk macosx clang++" -o build/macosx/x86_64/release/demo build/.objs/demo/macosx/x86_64/release/main.c.o -target x86_64-apple-macos12.3 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk -stdlib=libc++ -L/Users/zero/.xmake/packages/f/freetype/2.12.1/b49f1a356eae427ca7ad0ddf8af1b12b/lib -L/Users/zero/.xmake/packages/h/harfbuzz/5.3.1/c297384bf19f440e807dd60936cbcab1/lib -Wl,-x -lfreetype -lharfbuzz -lz

# 可以看到 freetype 正确的使用了 b49f1a356eae427ca7ad0ddf8af1b12b 的版本

# 运行可以看到依赖的库的代码被执行了> xmake run demo
ft_font_create call
hb_ft_font_create call

二、on_test 问题。

上面已经能够正常处理依赖循环,不过还是有些问题的,比如 freetype 是可选依赖 harfbuzz 我打开了 harfbuzz 支持但是在 packge 里的 on_test 里不会引用 harfbuzz 的头文件和库目录,尝试过手动添加 has_cfuncs 的 config 里的依赖方式是能正常的,现在只能把 freetype 的 on_test 关掉。

# freetype 开启 harfbuzz 选项时的 on_test 错误,只有 freetype 的 include

patching /Users/zero/.xmake/packages/f/freetype/2.12.1/b49f1a356eae427ca7ad0ddf8af1b12b/lib/pkgconfig/freetype.pc ..
> "/usr/bin/xcrun -sdk macosx clang" -c -Qunused-arguments -target x86_64-apple-macos12.3 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk -isystem /Users/zero/.xmake/packages/f/freetype/2.12.1/b49f1a356eae427ca7ad0ddf8af1b12b/include -o /var/folders/d8/53_r4vqx76953rk1ync3fg8c0000gn/T/.xmake501/221130/_EC0A8EEAA11F4920855474D29D1BA950.o /var/folders/d8/53_r4vqx76953rk1ync3fg8c0000gn/T/.xmake501/221130/_C97FAB220D604A32A6C366CE6F19EC6A.c
> checking for c includes(ft.h, hb.h)
> checking for c funcs(ft_font_create)
> checking for c links(freetype)
> checking for c snippet(has_cfuncs)
checkinfo: ...amdir/core/sandbox/modules/import/core/tool/compiler.lua:84: @programdir/modules/core/tools/gcc.lua:704: /var/folders/d8/53_r4vqx76953rk1ync3fg8c0000gn/T/.xmake501/221130/_C97FAB220D604A32A6C366CE6F19EC6A.c:3:10: fatal error: 'hb.h' file not found
#include <hb.h>

三、代码修改思路

先处理依赖的依赖的 requires_extra 不能被顶层的 add_requires 覆盖

  • xmake/modules/private/action/require/install.lua 里通过环境变量强制替换 requires_extra
  • xmake/modules/package/tools/xmake.lua 里把这个环境变量传递给新的 xmake

处理 _load_packages 依赖循环会卡住,当然是指默认依赖选项都开启会卡住。

  • xmake/modules/private/action/require/impl/package.lua 里的 _load_packages 创建一个表
  • 把当前的包全部放进去,然后检查 dep 里是否有已经处理过的,总之就是想办法让 dep 去找上层是否有过该包。

当然光是这样会出现 freetype 等 harfbuzz,harfbuzz 等 freetype 的情况

  • xmake/modules/private/action/require/install.lua 里发现有的包的 requires_extra 有 lazy_options 字段,就先用 lazy_options 来替换 requires_extra 。
  • 并在这次之后再把 requires_extra 替换回去,再一次编译。

@waruqi
Copy link
Member

waruqi commented Nov 30, 2022

这个问题不太好搞,等后面有空了,我再看下。

相关 issues: #3163

@waruqi waruqi transferred this issue from xmake-io/xrepo Nov 30, 2022
@waruqi
Copy link
Member

waruqi commented Dec 29, 2022

我觉得这个只能在 包里尽可能去避免这个循环依赖,xmake 是没法完全处理的。。

就算 load packages 里面通过判断之前加载过,打破了死循环加载了,但是还有很多其他问题的。。

比如加载过了,处理掉了死循环,目前顺序:freetype -> harfbuzz

  1. on_test 就是其一,最后一层的包 harfbuzz,on_test 肯定是失败了,没 freetype 信息,因为 freetype 还没安装
  2. on_install 里面,最后一层的包内部编译可能会失败,harfbuzz 依赖 freetype 里面的 includes 和 links ,但是 freetype 还没安装
  3. 还有最终提供给用户的 links order 也是个问题,这种得放进 whole-archive 才行

所以从xmake内部处理循环,目前不管怎么处理,都是会有各种问题的,我只能改进加载阶段,检测到循环依赖后,不让它死循环,然后提示警告或者错误信息,让用户在包层面去打破这种死循环。

所以 xmake-repo 仓库的 freetype 默认配置没开 harfbuzz,不会有这个问题,即使通过 add_configs 开了,也不会死循环,因为 harfbuzz 里面的 add_deps("freetype") 没开 harfbuzz。。

@waruqi
Copy link
Member

waruqi commented Dec 29, 2022

我 dev 加了个循环依赖检测,至少避免卡死。。

error: circular dependency(freetype.harfbuzz.freetype) detected in package(freetype)!

@zeromake
Copy link
Contributor Author

@waruqi
后面自己测试有些小问题修改,现在单独放 一个分支 ,暂时使用下来是没什么问题了,都能正常编译 freetype 和 harfbuzz,可以参考一下,不过实际上碰到这种问题很少的。

@zeromake
Copy link
Contributor Author

除了 on_test 不知道为啥一直不能加载依赖库以外都还行,我有空看看这个是我的分支改出的问题还是说 xmake 主分支上也有。

@waruqi
Copy link
Member

waruqi commented Dec 29, 2022

除了 on_test 不知道为啥一直不能加载依赖库以外都还行,我有空看看这个是我的分支改出的问题还是说 xmake 主分支上也有。

就是我刚说的问题,on_test 跑通的前提是 deps 必须要全部安装完成,才能 Fetch 到它们的 includedirs/links 。。但是循环依赖,不管怎么搞,都是不行的。

还有 on_install 里面也一样,foo/bar 两个库,相互依赖,编译安装 foo 需要 bar 里面的 头文件,编译安装 bar 需要 foo 里面的头文件,前提必须有一个完成安装,才能获取到,但是相互依赖导致,谁也没法通过编译。怎么解。。

@zeromake
Copy link
Contributor Author

@waruqi 我说的不是依赖循环的情况下出现的,而且我的依赖循环解决方案是

  1. 先进行一个 freetype(), harfbuzz(freetype) 编译。
  2. 再次重新进行一个 freetype(harfbuzz) 编译由于这个时候的 harfbuzz(freetype) 已经编译完成虽然是依赖循环但是并不会重新编译 harfbuzz(freetype) (因为 configs 没有变,还好 xmake 不会检查 harfbuzz 里的依赖是否变化)。

相当于我一次用安装好

add_requires("freetype")
add_requires("harfbuzz", {configs={freetype=true}})

然后再用

add_requires("freetype", {configs={harfbuzz=true}})
add_requires("harfbuzz", {configs={freetype=true}})

上面的情况必须先实现在根项目的 add_requires 里的 opt 可以传递到 repo 里的 add_requires 的相同包上。

@waruqi
Copy link
Member

waruqi commented Dec 30, 2022

这种感觉也不需要 xmake 改啥,还是调整包本身配置。。现有 xmake-repo 仓库的 freetype/harfbuzz 包。目前就是你这个流程,也不会死循环么

add_requires("freetype", {system = false, configs = {harfbuzz = true}})
add_requireconfs("freetype.harfbuzz", {system = false})
add_requireconfs("harfbuzz.freetype", {system = false})
in xmake-repo:
  -> python 3.10.6 [binary, from:ninja,meson]
  -> ninja 1.11.0 [binary, from:meson]
  -> meson 0.62.1 [from:harfbuzz]
  -> freetype#1 2.12.1 [from:harfbuzz]   -> 先编译 不带 harfbuzz 的 freetype
  -> harfbuzz 3.1.1 [from:freetype]   -> 再编译 harfbuzz(freetype)
  -> freetype 2.12.1 [harfbuzz:y] -> 再编译 freetype(harfbuzz)
please input: y (y/n/m)

线上 xmake 版本和 xmake-repo 包,啥也没改,就是这个流程,也不会死循环

@zeromake
Copy link
Contributor Author

@waruqi

试了一下确实能编译完成,但是却引用了两次 freetype 包。
先把 xmake 重置了

brew reinstall xmake
xmake update -s dev
xrepo add-repo local https://github.com/zeromake/xrepo.git

第一次先在 xmake.lua 里使用如下 add_requires

add_requires("freetype", {system = false})
add_requires("harfbuzz", {system = false, configs = {freetype = true}})
add_requireconfs("freetype.harfbuzz", {system = false, configs = {freetype = true}})
add_requireconfs("harfbuzz.freetype", {system = false})
> ~/D/p/demo xmake f -m debug -c                                                                                                                                                                       16:24:42
checking for platform ... macosx
checking for architecture ... x86_64
checking for Xcode directory ... /Applications/Xcode.app
checking for Codesign Identity of Xcode ... Apple Development: 390720046@qq.com (52L3LRH9JV)
checking for SDK version of Xcode for macosx (x86_64) ... 12.3
checking for Minimal target version of Xcode for macosx (x86_64) ... 12.3
note: install or modify (m) these packages (pass -y to skip confirm)?
in local:
  -> freetype 2.12.1 [from:harfbuzz]
  -> harfbuzz 5.3.1 [freetype:y]
please input: y (y/n/m)
y
  => install freetype 2.12.1 .. ok
  => install harfbuzz 5.3.1 .. ok

freetype(): 85ac3b35516943b781c80cb07c09a7aa
harfbuzz(freetype): e93a19f8f43d424fa87792d9ae11a3ad

第二次

add_requires("freetype", {system = false, configs = {harfbuzz = true}})
add_requires("harfbuzz", {system = false, configs = {freetype = true}})
add_requireconfs("freetype.harfbuzz", {system = false, configs = {freetype = true}})
add_requireconfs("harfbuzz.freetype", {system = false, configs = {harfbuzz = true}})
> ~/D/p/demo xmake f -m debug -c                                                                                                                                                                       16:25:29
checking for platform ... macosx
checking for architecture ... x86_64
checking for Xcode directory ... /Applications/Xcode.app
checking for Codesign Identity of Xcode ... Apple Development: 390720046@qq.com (52L3LRH9JV)
checking for SDK version of Xcode for macosx (x86_64) ... 12.3
checking for Minimal target version of Xcode for macosx (x86_64) ... 12.3
note: install or modify (m) these packages (pass -y to skip confirm)?
in local:
  -> freetype 2.12.1 [harfbuzz:y]
please input: y (y/n/m)
y
  => install freetype 2.12.1 .. ok

freetype(harfbuzz): 8fc32770f0f046cd93b679693b6bd118
harfbuzz(): 7b0f2332ef27421fa6aeb506bd796fb7 (这个有些问题不应该编译出来的吧,感觉还是 add_requireconfs 不能覆盖导致哪里有一个 harfbuzz 依赖,我印象里记得 xrepo 用 xmake 编译时会新的一个 xmake 命令,应该是编译 freetype 时里面的 xmake.lua, add_requires("harfbuzz") 导致的)

> ~/D/p/demo xmake build -rvD demo
"/usr/bin/xcrun -sdk macosx clang" -c -Qunused-arguments -target x86_64-apple-macos12.3 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk -g -O0 -std=c++17 -framework OpenGL -framework CoreVideo -framework CoreAudio -framework AudioToolbox -framework Carbon -framework CoreGraphics -framework ForceFeedback -framework Metal -framework AppKit -framework IOKit -framework CoreFoundation -framework Foundation -framework CoreHaptics -framework GameController -isystem /Users/zero/.xmake/packages/s/sdl2/2.26.1/53828060ac714d94a8a4ef29b0f6cd00/include -isystem /Users/zero/.xmake/packages/f/freetype/2.12.1/8fc32770f0f046cd93b679693b6bd118/include -isystem /Users/zero/.xmake/packages/h/harfbuzz/5.3.1/e93a19f8f43d424fa87792d9ae11a3ad/include -isystem /Users/zero/.xmake/packages/f/freetype/2.12.1/85ac3b35516943b781c80cb07c09a7aa/include -o build/.objs/demo/macosx/x86_64/debug/main.cpp.o main.cpp

"/usr/bin/xcrun -sdk macosx clang++" -o build/macosx/x86_64/debug/demo build/.objs/demo/macosx/x86_64/debug/main.cpp.o build/.objs/demo/macosx/x86_64/debug/ren-font.cpp.o -target x86_64-apple-macos12.3 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk -stdlib=libc++ -L/Users/zero/.xmake/packages/s/sdl2/2.26.1/53828060ac714d94a8a4ef29b0f6cd00/lib -L/Users/zero/.xmake/packages/f/freetype/2.12.1/8fc32770f0f046cd93b679693b6bd118/lib -L/Users/zero/.xmake/packages/h/harfbuzz/5.3.1/e93a19f8f43d424fa87792d9ae11a3ad/lib -L/Users/zero/.xmake/packages/f/freetype/2.12.1/85ac3b35516943b781c80cb07c09a7aa/lib -lsdl2 -lharfbuzz -lfreetype -framework OpenGL -framework CoreVideo -framework CoreAudio -framework AudioToolbox -framework Carbon -framework CoreGraphics -framework ForceFeedback -framework Metal -framework AppKit -framework IOKit -framework CoreFoundation -framework Foundation -framework CoreHaptics -framework GameController -liconv -lz

下面两个库都被引用了,顺序倒是对了。
freetype(harfbuzz): 8fc32770f0f046cd93b679693b6bd118
freetype(): 85ac3b35516943b781c80cb07c09a7aa

还有就是这用起来是真的麻烦,能不能先把覆盖 add_requires 选项先整一个,有了这个我就不用想哪个包依赖哪个了,例如

-- 第一次编译
add_requires("freetype", "harfbuzz")
add_require_globalconfs("freetype", {system = false})
add_require_globalconfs("harfbuzz", {system = false, configs={freetype = true}})
-- 第二次编译
add_requires("freetype", "harfbuzz")
add_require_globalconfs("freetype", {system = false, configs={harfbuzz = true}})
add_require_globalconfs("harfbuzz", {system = false, configs={freetype = true}})

package 里的 xmake.lua 里的 add_requires 可以先放一下。

@waruqi
Copy link
Member

waruqi commented Dec 30, 2022

为啥要搞两次,我上面贴的使用 xmake-repo 包,也是一次过。还有为啥不用 xmake-repo 仓库的包。

我印象里记得 xrepo 用 xmake 编译时会新的一个 xmake 命令

项目工程中包安装不用 xrepo 命令,不会有单独新xmake进程

感觉还是 add_requireconfs 不能覆盖导致哪里有一个 harfbuzz 依赖

覆盖什么?

还有就是这用起来是真的麻烦,能不能先把覆盖 add_requires 选项先整一个,有了这个我就不用想哪个包依赖哪个了,例如

看不懂你要搞啥。add_require_globalconfs 和 add_requireconfs 的区别是什么

@zeromake
Copy link
Contributor Author

zeromake commented Dec 30, 2022

@waruqi

为啥要搞两次,我上面贴的使用 xmake-repo 包,也是一次过。还有为啥不用 xmake-repo 仓库的包。

因为我自己的库不是 freetype 和 harfbuzz,只是情况类似罢了,如果全是 xmake 维护的就会这样。

项目工程中包安装不用 xrepo 命令,不会有单独新xmake进程

import("package.tools.xmake").install(package, configs)

package 里 on_install 掉起 xmake 实际是一个子进程,我说的就是这个 xmake f -c 的时候 xmake.lua 里的 add_requires 和父进程里我已经声明过的配置选项是不一致的。

覆盖什么?

比如 curl 依赖一个 http2 然后 http2 可选依赖 zlib

add_requires("curl", {system = false, configs={http2=true}})
add_requires("http2", {system = false, configs={zlib=true}})

但是依旧会编译一个 http2() 并连接它,我顶层已经有一个 http2(zlib) 了。

关键代码位置
这个时候 requires_extra 我没法直接继承顶层的。
我知道 add_requireconfs("curl.http2", {system = false, configs={zlib=true}}) 可以搞定,但是如果项目里有另一个依赖了 http2 那我是不是还要再加个 add_requireconfs("xxx.http2", {system = false, configs={zlib=true}}).

看不懂你要搞啥。add_require_globalconfs 和 add_requireconfs 的区别是什么

就是为了省去 add_requireconfs 需要指定父级包名。

@waruqi
Copy link
Member

waruqi commented Dec 30, 2022

package 里 on_install 掉起 xmake 实际是一个子进程,我说的就是这个 xmake f -c 的时候 xmake.lua 里的 add_requires 和父进程里我已经声明过的配置选项是不一致的。

目前包内部 xmake.lua 维护的是独立的工程,用户的 add_requireconfs 和包的 add_deps 是影响不到它的,你得自己透传进去。。比如通过 option,或者 patch

但是依旧会编译一个 http2() 并连接它,我顶层已经有一个 http2(zlib) 了。
这个时候 requires_extra 我没法直接继承顶层的。

这是两个独立的包,独立的链路,互不影响的,这个目前设计如此,你只能通过 add_requireconfs 修改 curl.http2 的配置,跟顶层保持一致

就是为了省去 add_requireconfs 需要指定父级包名。

可以模式匹配的,单层父包,add_requireconfs("*.http2") ,多层父包:add_requireconfs("**.http2")

@zeromake
Copy link
Contributor Author

@waruqi
好的我自己写个 option 把选项传递过去吧,add_requireconfs 有匹配模式也可以了

@waruqi
Copy link
Member

waruqi commented Dec 30, 2022

那先这样了,回头有问题再开

@waruqi waruqi closed this as completed Dec 30, 2022
@xmake-io xmake-io locked and limited conversation to collaborators Dec 30, 2022
@waruqi waruqi converted this issue into discussion #3231 Dec 30, 2022

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
Projects
None yet
Development

No branches or pull requests

2 participants