xmake icon indicating copy to clipboard operation
xmake copied to clipboard

feature-request:解决或提示静态库生成名称冲突

Open Stehsaer opened this issue 3 months ago • 13 comments

你在什么场景下需要该功能?

参考以下的项目示例:(基于xmake 3.0.4linux x86_64

xmake.lua

target("a::util")
	set_kind("static")
	add_files("a-util.cpp")
	add_headerfiles("a-util.hpp")

target("b::util")
	set_kind("static")
	add_files("b-util.cpp")
	add_headerfiles("b-util.hpp")

target("main")
	set_kind("binary")
	add_files("main.cpp")
	add_deps("a::util", "b::util")

main.cpp

#include <iostream>

#include "a-util.hpp"
#include "b-util.hpp"

int main()
{
	std::cout << a::util::func() << b::util::func() << '\n';
	return 0;
}

a-util.hpp

namespace a::util
{
	int func();
}

b-util.hpp 相似,不再重复

a-util.cpp

#include "a-util.hpp"

int a::util::func()
{
	return 1;
}

b-util.cpp 相似,不再重复

冲突情形

项目在编译时,静态库a::utilb::util的默认生成名称(basename)均为util,生成的库名称均为libutil.a。链接时,xmake在参数中生成了-lutil,但链接器的搜索路径下同时存在两个libutil.a,因此只链接了其中一个库。尽管这种情况可以通过设置set_basename("a-util")来解决,但是出现类似这种错误时,通常只会产生链接错误,用户没法第一时间想到生成名称的问题。

如果设置了set_policy("build.intermediate_directory", false),两个冲突的库会彼此覆盖,导致了相同问题。

链接参数:

/usr/local/bin/g++ -o build/linux/x86_64/release/main build/.objs/main/linux/x86_64/release/main.cpp.o -m64 -Lbuild/linux/x86_64/release/a -Lbuild/linux/x86_64/release/b -lutil

出现错误的提示:

❯ xmake build main   
[ 87%]: linking.release main
error: /usr/bin/ld: build/.objs/main/linux/x86_64/release/main.cpp.o: in function `main':
main.cpp:(.text+0x1e): undefined reference to `b::util::func()'
collect2: error: ld returned 1 exit status

描述可能的解决方案

潜在解决方案:

  1. 添加检查,出现冲突时自动停止编译
  2. 更改默认命名规则,将命名空间纳入

且这两种方案可以并行存在,见“候选方案”一节

描述你认为的候选方案

方案1

xmake自动检查目标文件名冲突情况,如果存在冲突则停止编译并发出告警。

示例:

> xmake build
error: conflicting basename for target a::util and b::util! Use set_basename to resolve the conflict, see documentation for usage

方案2

更改默认命名规则(例如,a::util的basename从util变更为a-utilb::util变更为b-util,分别生成liba-util.alibb-util.a)。这种方案可避免绝大部分情况下的冲突。

这种方案也可以和方案1并行,例如当存在a::utila-util时,两者的目标文件名同样发生了冲突,方案1同样可以发出告警,通知用户通过set_basename更改名称

其他信息

前文提到的项目示例:example.tar.gz

Stehsaer avatar Nov 06 '25 02:11 Stehsaer

https://xmake.io/zh/guide/project-configuration/namespace-isolation.html#namespace-isolation https://xmake.io/zh/api/description/global-interfaces.html#namespace

waruqi avatar Nov 06 '25 02:11 waruqi

https://xmake.io/zh/guide/project-configuration/namespace-isolation.html#namespace-isolation https://xmake.io/zh/api/description/global-interfaces.html#namespace

用文档里面的方法显式地指定如下:

xmake.lua

namespace("a")
	includes("a-util.lua")
namespace_end()

namespace("b")
	includes("b-util.lua")
namespace_end()

target("main")
	set_kind("binary")
	add_files("main.cpp")
	add_deps("a::util", "b::util")

a-util.lua

target("util")
	set_kind("static")
	add_files("a-util.cpp")
	add_headerfiles("a-util.hpp")

b-util.lua

target("util")
	set_kind("static")
	add_files("b-util.cpp")
	add_headerfiles("b-util.hpp")

产生了完全一致的行为,目标文件名均发生冲突。

Issue中的:

❯ xmake build -rv main
checking for gcc ... /usr/local/bin/gcc
checking for nim ... no
checking for g++ ... /usr/local/bin/g++
checking for the c++ compiler (cxx) ... g++
checking for /usr/local/bin/g++ ... ok
checking for flags (-fPIC) ... ok
[ 47%]: cache compiling.release main.cpp
/usr/local/bin/g++ -c -m64 -o build/.objs/main/linux/x86_64/release/main.cpp.o main.cpp
checking for flags (-MMD -MF) ... ok
checking for flags (-fdiagnostics-color=always) ... ok
checking for flags (-Wno-gnu-line-marker -Werror) ... ok
checking for the c++ compiler (cxx) ... g++
[ 47%]: cache compiling.release a::a-util.cpp
/usr/local/bin/g++ -c -m64 -o build/.objs/a/util/linux/x86_64/release/a-util.cpp.o a-util.cpp
checking for the c++ compiler (cxx) ... g++
[ 47%]: cache compiling.release b::b-util.cpp
/usr/local/bin/g++ -c -m64 -o build/.objs/b/util/linux/x86_64/release/b-util.cpp.o b-util.cpp
checking for ar ... /usr/bin/ar
checking for the static library archiver (ar) ... ar
[ 63%]: linking.release a::libutil.a
/usr/bin/ar -cr build/linux/x86_64/release/a/libutil.a build/.objs/a/util/linux/x86_64/release/a-util.cpp.o
checking for the static library archiver (ar) ... ar
[ 71%]: linking.release b::libutil.a
/usr/bin/ar -cr build/linux/x86_64/release/b/libutil.a build/.objs/b/util/linux/x86_64/release/b-util.cpp.o
checking for g++ ... /usr/local/bin/g++
checking for the linker (ld) ... g++
checking for flags (-fPIC) ... ok
[ 87%]: linking.release main
/usr/local/bin/g++ -o build/linux/x86_64/release/main build/.objs/main/linux/x86_64/release/main.cpp.o -m64 -Lbuild/linux/x86_64/release/a -Lbuild/linux/x86_64/release/b -lutil
/usr/bin/ld: build/.objs/main/linux/x86_64/release/main.cpp.o: in function `main':
main.cpp:(.text+0x1e): undefined reference to `b::util::func()'
collect2: error: ld returned 1 exit status
error: execv(/usr/local/bin/g++ -o build/linux/x86_64/release/main build/.objs/main/linux/x86_64/release/main.cpp.o -m64 -Lbuild/linux/x86_64/release/a -Lbuild/linux/x86_64/release/b -lutil) failed(1)

显式指定:

❯ xmake build -rv main
[ 31%]: cache compiling.release main.cpp
/usr/local/bin/g++ -c -m64 -o build/.objs/main/linux/x86_64/release/main.cpp.o main.cpp
[ 39%]: cache compiling.release a::a-util.cpp
/usr/local/bin/g++ -c -m64 -o build/.objs/a/util/linux/x86_64/release/a-util.cpp.o a-util.cpp
[ 47%]: cache compiling.release b::b-util.cpp
/usr/local/bin/g++ -c -m64 -o build/.objs/b/util/linux/x86_64/release/b-util.cpp.o b-util.cpp
checking for flags (-MMD -MF) ... ok
checking for flags (-fdiagnostics-color=always) ... ok
checking for flags (-Wno-gnu-line-marker -Werror) ... ok
[ 55%]: linking.release b::libutil.a
/usr/bin/ar -cr build/linux/x86_64/release/b/libutil.a build/.objs/b/util/linux/x86_64/release/b-util.cpp.o
[ 63%]: linking.release a::libutil.a
/usr/bin/ar -cr build/linux/x86_64/release/a/libutil.a build/.objs/a/util/linux/x86_64/release/a-util.cpp.o
[ 79%]: linking.release main
/usr/local/bin/g++ -o build/linux/x86_64/release/main build/.objs/main/linux/x86_64/release/main.cpp.o -m64 -Lbuild/linux/x86_64/release/a -Lbuild/linux/x86_64/release/b -lutil
/usr/bin/ld: build/.objs/main/linux/x86_64/release/main.cpp.o: in function `main':
main.cpp:(.text+0x1e): undefined reference to `b::util::func()'
collect2: error: ld returned 1 exit status
error: execv(/usr/local/bin/g++ -o build/linux/x86_64/release/main build/.objs/main/linux/x86_64/release/main.cpp.o -m64 -Lbuild/linux/x86_64/release/a -Lbuild/linux/x86_64/release/b -lutil) failed(1)

因为问题的根源并不是namespace中发生了冲突,我认为xmake是正确处理了namespace的,a::utilb::util也能够正确地解析出依赖关系,只是编译中由于basename等发生冲突,在不同目录下生成了同名的.a静态库,最后导致链接时的查找问题

Stehsaer avatar Nov 06 '25 03:11 Stehsaer

自己 set_filename/set_basename 改下就行了,你 target name 一样,默认的 basename 当然相同了,target 里面不要自己去用 :: 那个是给 namespace 用的,要么你用其他符号。。

waruqi avatar Nov 06 '25 05:11 waruqi

自己 set_filename/set_basename 改下就行了,你 target name 一样,默认的 basename 当然相同了,target 里面不要自己去用 :: 那个是给 namespace 用的,要么你用其他符号。。

  • 提这个Issue是希望
    • 讨论这个target名称冲突的问题,提出feature-request,并给出我的方案设想
    • 不是该不该用set_basename或者target("..")中名称是否规范的问题。无论名称书写是否规范,都是产生了同样的问题——引入namespace后,存在target重名的风险,而文档内似乎是没有明显说明这一点,发生重名时xmake也没有提供明显的提示
  • 不采用方案2是因为namespace的设计出发点问题吗?我这边的看法是,如果namespace是为了隔离同名的target, rule等,那我们会希望xmake也“自动”能(或者通过某个policy/rule开启)帮忙避免同名target产生同名输出的问题(这个问题在关闭了intermediate directory后更加明显了,同名输出会互相覆写)
  • 关于方案1您怎么看?发生同名target时提醒用户,一是提供一个“双重保险”的作用,二是提升使用/Debug体验,无需翻找文档,一看就能知道问题

Stehsaer avatar Nov 06 '25 06:11 Stehsaer

不采用方案2是因为namespace的设计出发点问题吗?我这边的看法是,如果namespace是为了隔离同名的target, rule等,那我们会希望xmake也“自动”能(或者通过某个policy/rule开启)帮忙避免同名target产生同名输出的问题(这个问题在关闭了intermediate directory后更加明显了,同名输出会互相覆写)

namespace 已经用了 ::

这种自动不会做,自己手动通过 set_basename 调整,自动处理要处理很多的细节,弄的不好,反而引入更多的坑。

  1. 如果这两 target 不在一个依赖链上,不会冲突,这个时候不用改名
  2. 如果一个依赖链上存在冲突,才需要自动改文件名
  3. 如果自动改了文件名,又会影响 xmake install 的安装文件名,到时候用户又是一堆 issues 过来,自动命名不可控,不是所有用户都认可这种需求,有的人不想改,有的认为 xxx-utils.a ,那 xmake 不管怎么自动命名,总会有一部分用户不满足需求会提 issues 过来。

所以还不如用户手动 set_basename 完全可控。

关于方案1您怎么看?发生同名target时提醒用户,一是提供一个“双重保险”的作用,二是提升使用/Debug体验,无需翻找文档,一看就能知道问题

现在最后 link 阶段,linker 本身就会报错,当然前期前期检测报错当然会更友好,但这需求目前优先级不高,等后面有时间也许会考虑。毕竟这种检测本身也会影响性能,尤其在 target 数非常多的时候,为了这极少遇到的 case,每次都要额外去检测一遍,也有点的得不偿失,毕竟 linker 本身也会报错提示。

waruqi avatar Nov 06 '25 07:11 waruqi

Bot detected the issue body's language is not English, translate it automatically.


Title: feature-request: resolve or prompt static library generation name conflict

Issues-translate-bot avatar Nov 06 '25 12:11 Issues-translate-bot

Bot detected the issue body's language is not English, translate it automatically.


Just change set_filename/set_basename yourself. Your target name is the same, and the default basename is of course the same. Don't use it yourself in the target:: That one is for namespace, or you use other symbols. .

Issues-translate-bot avatar Nov 06 '25 12:11 Issues-translate-bot

Bot detected the issue body's language is not English, translate it automatically.


Just change set_filename/set_basename yourself. Your target name is the same, and the default basename is of course the same. Don’t use it yourself in the target:: That one is for namespace, or you use other symbols. .

  • I raise this issue out of hope
    • Discuss the problem of target name conflict, raise feature-request, and give my solution idea
    • It is not a question of whether set_basename should be used or whether the name in target("..") is standardized. Regardless of whether the name writing is standardized or not, the same problem arises - after the namespace is introduced, there is a risk of duplicate target names, but this does not seem to be clearly stated in the document, and xmake does not provide an obvious prompt when duplicate names occur.
  • Is it because of the design starting point of the namespace that Option 2 is not adopted? My point of view is that if the namespace is to isolate targets, rules, etc. with the same name, then we hope that xmake can also "automatically" (or be turned on through a certain policy/rule) to help avoid the problem of the target with the same name producing output with the same name (this problem becomes more obvious after closing the intermediate directory, and the output with the same name will overwrite each other)
  • What do you think about option 1? When a target with the same name occurs, the user is reminded. The first is to provide a "double insurance" effect, and the second is to improve the use/debug experience. There is no need to search for documents, and the problem can be known at a glance.

Issues-translate-bot avatar Nov 06 '25 12:11 Issues-translate-bot

Bot detected the issue body's language is not English, translate it automatically.


Is it because of the design starting point of the namespace that Option 2 is not adopted? My opinion is that if the namespace is to isolate targets, rules, etc. with the same name, then we hope that xmake can also "automatically" (or be turned on through a certain policy/rule) to help avoid the problem of the target with the same name producing output with the same name (this problem becomes more obvious after closing the intermediate directory, and the output with the same name will overwrite each other)

namespace already used ::

This cannot be done automatically. You have to manually adjust it through set_basename. Automatic processing requires a lot of details. If it is not done well, it will introduce more pitfalls.

  1. If the two targets are not in the same dependency chain, there will be no conflict, and there is no need to change the name at this time.
  2. If there is a conflict in a dependency chain, the file name needs to be automatically changed.
  3. If the file name is automatically changed, it will affect the installation file name of xmake install. When the time comes, users will have a bunch of issues. Automatic naming is uncontrollable. Not all users agree with this demand. Some people do not want to change it, and some think xxx-utils.a. No matter how xmake automatically names it, there will always be some users who do not meet the needs and will raise issues.

So it is not as fully controllable as manual set_basename by the user.

What do you think about option 1? When a target with the same name occurs, the user is reminded. The first is to provide a "double insurance" effect, and the second is to improve the use/debug experience. There is no need to search for documents, and the problem can be known at a glance.

Now in the final link stage, the linker itself will report an error. Of course, it will be more friendly to detect errors in the early stage. However, this requirement is not a high priority at the moment. We may consider it when we have time later. After all, this kind of detection itself will also affect the performance, especially when the number of targets is very large. For this rarely encountered case, it is necessary to perform an additional detection every time, which is not worth the gain. After all, the linker itself will also report an error message.

Issues-translate-bot avatar Nov 06 '25 12:11 Issues-translate-bot

其实解决这个问题的最好办法,不是重命名,而是用全路径库文件替代 -L/-l 去做 link ,不过这种方式,不一定所有链接器都支持,我暂时只对 gcc/clang/link.exe 做了支持,可以尝试下这个 patch

https://github.com/xmake-io/xmake/pull/7011

可通过 build.link_target_with_fullpath 策略配置开启或者关闭,目前默认开启了,先观察下兼容性如何,如果不行,就默认关闭,遇到冲突了,手动开。

waruqi avatar Nov 10 '25 02:11 waruqi

Bot detected the issue body's language is not English, translate it automatically.


In fact, the best way to solve this problem is not to rename it, but to use the full path library file instead of -L/-l to do link. However, this method may not be supported by all linkers. I only support gcc/clang/link.exe for the time being. You can try this patch.

https://github.com/xmake-io/xmake/pull/7011

It can be turned on or off through the build.link_target_with_fullpath policy configuration. It is currently turned on by default. Check the compatibility first. If not, it will be turned off by default. If there is a conflict, turn it on manually.

Issues-translate-bot avatar Nov 10 '25 05:11 Issues-translate-bot

其实解决这个问题的最好办法,不是重命名,而是用全路径库文件替代 -L/-l 去做 link ,不过这种方式,不一定所有链接器都支持,我暂时只对 gcc/clang/link.exe 做了支持,可以尝试下这个 patch

#7011

可通过 build.link_target_with_fullpath 策略配置开启或者关闭,目前默认开启了,先观察下兼容性如何,如果不行,就默认关闭,遇到冲突了,手动开。

这种方式实现上目前还存在不少问题,不太好搞,先放着吧。

waruqi avatar Nov 10 '25 13:11 waruqi

Bot detected the issue body's language is not English, translate it automatically.


In fact, the best way to solve this problem is not to rename it, but to use the full path library file instead of -L/-l to do link. However, this method may not be supported by all linkers. I only support gcc/clang/link.exe for the time being. You can try this patch.

#7011

It can be turned on or off through the build.link_target_with_fullpath policy configuration. It is currently turned on by default. Check the compatibility first. If not, it will be turned off by default. If there is a conflict, turn it on manually.

There are still many problems in the implementation of this method, which are not easy to solve, so let’s leave it alone for now.

Issues-translate-bot avatar Nov 10 '25 13:11 Issues-translate-bot