Skip to content

Support customizing implib path of MinGW/MSVC#6334

Closed
SineStriker wants to merge 11 commits intoxmake-io:devfrom
SineStriker:dev
Closed

Support customizing implib path of MinGW/MSVC#6334
SineStriker wants to merge 11 commits intoxmake-io:devfrom
SineStriker:dev

Conversation

@SineStriker
Copy link
Contributor

@SineStriker SineStriker commented Apr 18, 2025

#3052
我很久以前问过关于设置一个动态库目标dlllib文件输出目录分离的问题,当时作者给的答复是不希望给 MSVC 特有的功能设置新的 API,当时比较忙就没继续跟进。

这段时间读了很久源码,大致知道了 xmake 工作流程,发现了一些问题。

  1. xmake 源码里有超过 10 处位置对.lib或者.dll.a进行了特殊处理,而且工具链 nvcc (mingw)、link (msvc)、gcc (mingw) 都被涉及,而且有多处特殊处理是我上次提 issue 之后添加的,说明不止我一个人遇到了与之相关的问题;
  2. xmake 工程安装可以在set_prefixdir里设置bindirlibdirdlllib的安装目录分离了,这个功能是去年添加的,这就会导致一个更加匪夷所思的问题,为什么安装的时候允许分离但构建的时候不允许?我暂且认为这是 xmake 设计之初没有考虑周全,CMake 的构建和安装就统一对一个目标的潜在输出文件分为了 RUNTIME/LIBRARY/ARCHIVE 三种类型(虽然我觉得 ARCHIVE 纯粹没必要),我认为一个目标的构建和安装目录的配置最好有一致性,让项目的构建目录结构和安装目录结构保持相同,可以减少出问题的概率;
  3. 关于clean的问题是我上次提的,f40cd6da49ed65067d4c6060fa3605cdbd994499 这个提交只处理了 MSVC 的输出文件,我补充了 MinGW 的;

结论,我还是希望作者能考虑一下直接支持构建期dlllib输出分离。
目前我是通过扩展set_targetdir实现的,在后面添加一个形如{ implibdir = "$(buildir)/lib" }的 option table 就可以完成,对 msvc/gcc 的 build、clean 和 install 都进行了测试。当然这样一个接口看起来不完美,跟 install 时候的 api 名称不一样,之后名称怎么改可以讨论。如果这个 option 不加,那么整个 xmake 的逻辑与以前完全相同。

目前还差一个小问题,就是修改了implib的输出目录以后,还要对工程里其他所有依赖这个动态库的目标增加一个linkdir,这个暂时还没实现,目测应该加在inherit_link这个模块里。

Sample:

add_rules("mode.debug")
set_prefixdir("", { bindir = "bin", libdir = "lib64" })

target("hello")
    set_kind("shared")
    add_files("hello.cpp")
    set_targetdir("$(buildir)/bin", { implibdir = "$(buildir)/lib" }) -- 设置 implib 的输出路径
    add_linkdirs("$(buildir)/lib", { interface = true }) -- 现在必须设置 linkdir,否则 main 就会链接失败
target_end()

target("main")
    set_kind("binary")
    add_files("main.cpp")
    add_deps("hello")
    set_targetdir("$(buildir)/bin")
target_end()

@SineStriker
Copy link
Contributor Author

还发现一个问题:在modules/core/tools/xxx里的linkargv才是完整的构建参数,但是如果使用xmake project -k ninja生成ninja脚本,里面并不包含这些完整的构建参数。

@Issues-translate-bot
Copy link

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


I also found a problem: the linkargv in modules/core/tools/xxx is the complete build parameters, but if you use xmake project -k ninja to generate a ninja script, it does not contain these complete build parameters.

@waruqi
Copy link
Member

waruqi commented Apr 19, 2025

xmake 源码里有超过 10 处位置对.lib或者.dll.a进行了特殊处理,而且工具链 nvcc (mingw)、link (msvc)、gcc (mingw) 都被涉及,而且有多处特殊处理是我上次提 issue 之后添加的,说明不止我一个人遇到了与之相关的问题;

其中大部分都是找库的库名处理,linkname 处理,没看到 #3052 和你遇到的问题有什么关系。mingw 下库名很多都是 dll.a ,而不是 .lib 当然得处理下。

xmake 工程安装可以在set_prefixdir里设置bindir、libdir把dll和lib的安装目录分离了,这个功能是去年添加的,这就会导致一个更加匪夷所思的问题,为什么安装的时候允许分离但构建的时候不允许?

安装是安装,构建是构建,构建只需要保证 xmake run 能加载运行,没必要做各种复杂的分离,安装跟系统环境相关。
另外,也没说不允许,你照样可以通过 set_targetdir 去分离存储,只是没必要,用户非要这么干,我也干扰不了,反正接口都提供了。

我只是说 implib 这玩意,暂时没必要单独提供 api 去配置路径,安装的 set_prefixdir ,也不支持,并没有区别对待。

我暂且认为这是 xmake 设计之初没有考虑周全,CMake 的构建和安装就统一对一个目标的潜在输出文件分为了 RUNTIME/LIBRARY/ARCHIVE 三种类型(虽然我觉得 ARCHIVE 纯粹没必要),我认为一个目标的构建和安装目录的配置最好有一致性,让项目的构建目录结构和安装目录结构保持相同,可以减少出问题的概率;

安装和构建,不可能完全一致,目的完全不同,怎么一致,就比如 qt 还有 qtdeploy 了,ios app 安装还要先打包成 .ipa ,android 安装还得先打包成 .apk 。。难道每次构建运行,也得都走一遍 qtdeploy ,导出全部 dll 和资源文件后,再运行?这得多慢,改一行代码,deploy 半天,才能出效果。

编译个 ios app 还得先打包出 .ipa 在运行?

构建阶段,不管用户怎么配置路径,只要保证 xmake run 能处理好环境,快速加载出效果就行了。目的是调试和开发效率。

安装阶段,根据系统和用户需要,配置实际的安装结构,配置打包格式,配置签名,配置资源打包一堆事情。

@Issues-translate-bot
Copy link

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


There are more than 10 locations in the xmake source code that have special processing for .lib or .dll.a, and the tool chains nvcc (mingw), link (msvc), and gcc (mingw) are all involved. There are many special processing that I added after the last time I mentioned the issue, which means that I am not the only one who encountered the related problems;

Most of them are processed by looking for library names and linkname processing. I didn’t see what does #3052 have to do with the problems you are facing. Many of the library names under mingw are dll.a, not .lib, of course, they have to be processed.

xmake project installation can set bindir and libdir in set_prefixdir to separate the installation directories of dll and lib. This function was added last year, which will lead to an even more incredible question. Why is separation allowed during installation but not during construction?

Installation is installation, and construction is construction. Construction only requires ensuring that xmake run can be loaded and run. There is no need to do various complex separations. The installation is related to the system environment.
In addition, it was not allowed. You can still separate the storage through set_targetdir. It's just that there is no need. The user has to do this, and I can't interfere. Anyway, the interface is provided.

I just said that there is no need to provide the API to configure the path of the implib thing for the time being. The installed set_prefixdir is not supported, and there is no difference.

I think this is not considered thoroughly at the beginning of the design of xmake. The construction and installation of CMake are divided into three types: RUNTIME/LIBRARY/ARCHIVE (although I think ARCHIVE is purely unnecessary). I think the configuration of a target's construction and installation directory should be consistent, so that the project's construction directory structure and installation directory structure can be kept the same, which can reduce the probability of problems;

Installation and construction cannot be completely consistent, the purpose is completely different, how to be consistent, for example, qt and qtdeploy, the installation of ios app must be packaged into .ipa first, and the installation of android must be packaged into .apk first. . Do you have to go through qtdeploy every time you build and run it, export all the dlls and resource files, and then run it? How slow is this? Change a line of code and deploy it for half a day before it can produce an effect.

To compile an ios app, you have to package out .ipa first and run it?

During the construction stage, no matter how the user configures the path, just ensure that xmake run can handle the environment well and quickly load the effect. The purpose is debugging and development efficiency.

During the installation stage, according to the system and user needs, the actual installation structure is configured, the packaging format is configured, the signature is configured, and the resource is configured to package a lot of things.

@waruqi
Copy link
Member

waruqi commented Apr 19, 2025

还发现一个问题:在modules/core/tools/xxx里的linkargv才是完整的构建参数,但是如果使用xmake project -k ninja生成ninja脚本,里面并不包含这些完整的构建参数。

这种有问题,可以单开 issues ,提供复现 case ,到时候调下就行了。

@Issues-translate-bot
Copy link

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


Another problem was found: the linkargv in modules/core/tools/xxxis the complete build parameters, but if you usexmake project -k ninja` to generate a ninja script, it does not contain these complete build parameters.

If you have any problems, you can open issues alone and provide reproduction case. Then you can adjust it.

@waruqi
Copy link
Member

waruqi commented Apr 19, 2025

另外,#3052 这个问题,按理应该没必要去分离 implib 路径的,尽管库同名,第二次编译生成 .lib 后,应该也会覆盖掉,确保 link 通过才行,并不会冲突。。感觉之前只是没有深入排查定位根本原因而已。

所以大部分情况下,都是没必要单独设置 implib 路径的,也没见哪个 vs 工程会去单独设置,如果只是为了解 #3052 ,可以重开后,回头有空细调下原因就行了

@Issues-translate-bot
Copy link

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


In addition, there is no need to separate the impllib path for #3052 problem, although the library has the same name, it should also be overwritten after the second compilation and generation of .lib to ensure that the link is passed. . I feel that I just didn’t thoroughly investigate and locate the root cause before.

@waruqi
Copy link
Member

waruqi commented Apr 19, 2025

当然,这个 patch 还是可以保留,不过需要做进一步改进,既然有了 target:implibdir(), 那就再加上 target:implibfile() 去内部封装 .dll.a , .lib 不用每个地方都去 拼一遍。

其他的地方,我暂时没时间 review 等回头有时间在看。

@Issues-translate-bot
Copy link

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


Of course, this patch can still be retained, but further improvements are needed. Since there is target:implidir(), then add target:implibfile() to internally encapsulate .dll.a and .lib without spelling it every place.

Other places, I don’t have time to review. I’m waiting for the time to look back.

@SineStriker
Copy link
Contributor Author

安装和构建,不可能完全一致,目的完全不同,怎么一致,就比如 qt 还有 qtdeploy 了,ios app 安装还要先打包成 .ipa ,android 安装还得先打包成 .apk 。。难道每次构建运行,也得都走一遍 qtdeploy ,导出全部 dll 和资源文件后,再运行?这得多慢,改一行代码,deploy 半天,才能出效果。

  • 我感觉我们说的不在同一个频道上。首先 ios 和 android 不是电脑的项目,不在讨论范围之内,对于 qt 项目来说,确实有一些 qt 项目是每一次 build 都执行一次 deploy 的,deploy 在 windows 上还是挺快的。
  • 一个项目里可能有很多目标,我希望至少保证到我自己在项目里添加的目标(我自己添加的目标),在构建期的目录结构跟安装的相同,对于 qt 库和插件那些东西,那不是我从这个项目里添加进去的。
  • 一个目标它可能有不止一个输出文件,就比如 dll 还会输出一个 implib,现在是直接不给我控制它输出路径的机会,MSVC 我可以手动添加/IMPLIB:去改,但是 MinGW 直接在 xmake 里写死了它的输出路径。

也没说不允许,你照样可以通过 set_targetdir 去分离存储

我只是说 implib 这玩意,暂时没必要单独提供 api 去配置路径,安装的 set_prefixdir ,也不支持,并没有区别对待。

  • 在分类上,implib 和 .a、.lib 这些静态库一样,属于 archive 文件(至少 CMake 是这么区分的)。xmake 现在来说其实就是对构建期和安装期区别对待了,set_prefixdir 就是能精确控制 implib 的安装路径,但是 set_targetdir 做不到控制 implib 的构建输出路径,这个东西也不是 MSVC 特有的,而是 Windows 上包括 mingw、clang-cl 在内所有的编译器都有的东西。
  • 而且 implib 和其他的编译输出文件还不一样,比如 MSVC 还有 ilk、manifest 文件,但这些 xmake 都没管,用户可以自由用编译参数来控制它们,而且这些产物通常不需要安装,但是只有 implib 是不仅被 xmake 接管而且还需要安装的,xmake 不仅接管了而且还没给用户提供输出路径的接口,我觉得是有问题的。
  • 而且从我去看下来,增加一个支持 implib 输出路径的功能也不是特别麻烦的工作,而且还能让 xmake 内部那一堆特殊处理更加统一。

这个 PR 先留着了,我这段时间会测试它跟 xmake 的其他功能是不是有水土不服,会不时添加新的提交的。目前找到的问题就是以下几个:

  • 生成其他构建系统的文件(比如 ninja)的时候,生成进去的不是完整的链接参数(这个应该是一直以来就有的问题,只是没人发现)
  • 我加的这个 implib 参数还不能自动添加 linkdir,得手动声明一下,而且我注意到因为 xmake 3.0 之前没有系统性的依赖关系图,所以在脚本域里添加的 link 依赖不能完美地传播到其他目标里去。

@Issues-translate-bot
Copy link

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


Installation and construction cannot be completely consistent, the purpose is completely different, how to consistent, for example, qt and qtdeploy, the ios app installation must be packaged into .ipa first, and android installation must be packaged into .apk first. . Do you have to go through qtdeploy every time you build and run it, export all the dlls and resource files, and then run it? How slow is this? Change a line of code and deploy it for half a day before it can produce an effect.

  • I feel like we are not on the same channel. First of all, ios and android are not computer projects and are not within the scope of discussion. For qt projects, there are indeed some qt projects that execute deploy once every build. deploy is quite fast on Windows.
  • There may be many goals in a project. I hope to at least ensure that the goals I added to the project (the goals I added myself) are added to the project. The directory structure during the build period is the same as the ones I installed. For qt libraries and plug-ins, that is not what I added from this project.
  • A target may have more than one output file, such as dll will also output an implib. Now it will not give me the chance to control its output path. MSVC I can manually add /IMPLIB: to modify it, but MinGW directly writes its output path in xmake.

It's not allowed, you can still use set_targetdir to separate the storage

I just said that there is no need to provide the API to configure the path separately for the impllib thing. The installed set_prefixdir is not supported, and there is no difference.

  • In terms of classification, implib is the archive file like .a and .lib (at least CMake is so differentiated). xmake is actually treating the differentiation between the build period and the installation period. Set_prefixdir can accurately control the installation path of implib, but set_targetdir cannot control the construction output path of implib. This thing is not unique to MSVC, but something that all compilers on Windows, including mingw and clang-cl.
  • Moreover, implib is different from other compiled output files, such as MSVC and ilk and manifest files, but these xmakes are not in charge. Users can control them freely with compiled parameters. These products usually do not need to be installed, but only implib is not only taken over by xmake but also needs to be installed. xmake not only takes over but also does not provide the user with an interface for output path. I think there is a problem.
  • And from my perspective, adding a function that supports the implib output path is not particularly troublesome, and it can also make the special processing inside xmake more unified.

I will keep this PR for now. During this period, I will test whether it is not suitable for the other functions of xmake and will add new submissions from time to time. The questions found are the following:

  • When generating files of other build systems (such as ninja), the generated link parameters are not complete (this should be a problem that has always been there, but no one has discovered it)
  • The implib parameter I added cannot be automatically added to linkdir, so I have to declare it manually. And I noticed that because there was no systematic dependency diagram before xmake 3.0, the link dependencies added to the script domain cannot be perfectly propagated to other targets.

@SineStriker
Copy link
Contributor Author

当然,这个 patch 还是可以保留,不过需要做进一步改进,既然有了 target:implibdir(), 那就再加上 target:implibfile() 去内部封装 .dll.a , .lib 不用每个地方都去 拼一遍。

其他的地方,我暂时没时间 review 等回头有时间在看。

感谢,确实需要做改进,现在这个 api 也是我临时想的而已。

我觉得最理想的情况是能让构建期和安装期有形式相同的 API,以 CMake 举例:

  • 构建期:
set_target_properties(${PROJECT_NAME} PROPERTIES
    PDB_OUTPUT_DIRECTORY     ${CMAKE_BINARY_DIR}/bin        # .pdb
    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/bin  # .exe, Unix Executable
    LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/lib  # .so, .dylib
    ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/lib  # .a, .lib, .dll.a
)
  • 安装期:
install(TARGETS ${PROJECT_NAME} 
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
)

从 API 设计之初就考虑了一个目标潜在的多个输出文件并对它们都设置了专用的 output dir,install 的时候 DESTINATION 也能保持一致,但是考虑到 xmake 现在已经有 set_targetdir 这样的 API 了,要跟 set_prefixdir 保持一致感觉还是挺困难的,怎么改进现在我也没想好。

@Issues-translate-bot
Copy link

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


Of course, this patch can still be retained, but further improvements are needed. Since there is target:implidir(), then add target:implibfile() to internally encapsulate .dll.a and .lib without spelling it every place.

Other places, I don’t have time to review. I have time to look back.

Thanks, I do need to make improvements, and now this API is just what I think about temporarily.

I think the ideal situation is to have the same API in the build and install periods, taking CMake as an example:

  • Build period:
set_target_properties(${PROJECT_NAME} PROPERTIES
    PDB_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin # .pdb
    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/bin # .exe, Unix Executable
    LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/lib # .so, .dylib
    ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/lib # .a, .lib, .dll.a
)
  • Installation period:
install(TARGETS ${PROJECT_NAME}
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
)

Since the beginning of the API design, we have considered multiple potential output files for a target and set a dedicated output dir for them. DESTINATION can also be consistent when installing it, but considering that xmake now has an API like set_targetdir, it feels quite difficult to keep it consistent with set_prefixdir. I haven't thought about how to improve it now.

@waruqi
Copy link
Member

waruqi commented Apr 19, 2025

我感觉我们说的不在同一个频道上。首先 ios 和 android 不是电脑的项目,不在讨论范围之内,对于 qt 项目来说,确实有一些 qt 项目是每一次 build 都执行一次 deploy 的,deploy 在 windows 上还是挺快的。

我只是举例,我的意思的 qt 项目也压根没必要 build 阶段去 deploy ,即使 win 上快,但相比不去 deploy ,开发调试效率会更高。其他pc项目也是,压根没必要去 copy 各种 dll/so 到指定目录,xmake run 会直接加载 path 去确保快速加载运行。

尤其是一些依赖大量 dll/so 的程序,每次 build 都会省去一大笔 dll/so copy 的时间。

一个项目里可能有很多目标,我希望至少保证到我自己在项目里添加的目标(我自己添加的目标),在构建期的目录结构跟安装的相同,对于 qt 库和插件那些东西,那不是我从这个项目里添加进去的。

那你可以自己通过 set_targetdir 配置,一个 target 要么只能是 shared ,要么只能是 static ,原本就是可以自己配置的。

现在仅仅 implib 不支持而已。

一个目标它可能有不止一个输出文件,就比如 dll 还会输出一个 implib,现在是直接不给我控制它输出路径的机会,MSVC 我可以手动添加/IMPLIB:去改,但是 MinGW 直接在 xmake 里写死了它的输出路径。

after_link 里面,你自己 os.cp 同样可以。机会同样给了。

在分类上,implib 和 .a、.lib 这些静态库一样,属于 archive 文件(至少 CMake 是这么区分的)。xmake 现在来说其实就是对构建期和安装期区别对待了,set_prefixdir 就是能精确控制 implib 的安装路径,但是 set_targetdir 做不到控制 implib 的构建输出路径,这个东西也不是 MSVC 特有的,而是 Windows 上包括 mingw、clang-cl 在内所有的编译器都有的东西。

set_prefixdir 那个是通过设置 dll 路径,implib 也只是跟随 dll 的路径,并不能分离。效果 跟 set_targetdir 一样的,你 set_prefixdir 设置 dll 的路径,对应 implib 也会跟着修改调整。 并没有区别,效果完全一致。

而且 implib 和其他的编译输出文件还不一样,比如 MSVC 还有 ilk、manifest 文件,但这些 xmake 都没管,用户可以自由用编译参数来控制它们,而且这些产物通常不需要安装,但是只有 implib 是不仅被 xmake 接管而且还需要安装的,xmake 不仅接管了而且还没给用户提供输出路径的接口,我觉得是有问题的。

我之前说了,这些平台和编译特有的临时文件,xmake 永远不可能全部去为它们单独开接口。但不代表不能控制他们。现在就可以。。

after_link., after_build 里面你可以通过 os.cp + rule 做你想做的任何事情,想怎么 copy 到指定位置,都可以

而且从我去看下来,增加一个支持 implib 输出路径的功能也不是特别麻烦的工作,而且还能让 xmake 内部那一堆特殊处理更加统一

implib 至少 mingw/ msvc 以及安装上都也许会对外导出用到,想要通过 implibdir 配置,倒也可以,但是 ilk exp 以及其他乱七八糟的文件 就不要想了,不可能单独加接口的。

@Issues-translate-bot
Copy link

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


I feel like we are not talking about the same channel. First of all, ios and android are not computer projects and are not within the scope of discussion. For qt projects, there are indeed some qt projects that execute deploy once every build. deploy is quite fast on Windows.

I'm just giving an example. I mean that there is no need to deploy in the build stage at all. Even if win is fast, it will be more efficient than not deploying. The same is true for other PC projects. There is no need to copy various dll/so to the specified directory at all. xmake run will directly load the path to ensure quick loading and running.

Especially some programs that rely on a large number of dll/so, each build will save you a lot of time in dll/so copy.

There may be many goals in a project. I hope to at least ensure that the goals I added to the project (the goals I added myself) are the same as those installed during the build period. For things like qt libraries and plug-ins, that is not what I added from this project.

Then you can configure it yourself through set_targetdir. A target can either be shared or static. It can be configured by yourself.

Now only implib is not supported.

A target may have more than one output file, such as dll will also output an implib. Now it will not give me the chance to control its output path. MSVC can manually add /IMPLIB: to modify it, but MinGW directly writes its output path in xmake.

In after_link, you can also do so with os.cp. Opportunities are also given.

In terms of classification, implib is like static libraries such as .a and .lib, and belong to archive files (at least CMake is so differentiated). xmake is actually treating the differentiation between the build period and the installation period. Set_prefixdir can accurately control the installation path of implib, but set_targetdir cannot control the construction output path of implib. This thing is not unique to MSVC, but something that all compilers on Windows, including mingw and clang-cl.

set_prefixdir is set by setting the dll path, implib only follows the dll path and cannot be separated. The effect is the same as set_targetdir. If you set_prefixdir set the path of the dll, the corresponding implib will also be modified and adjusted. There is no difference, the effect is exactly the same.

And implib is different from other compiled output files, such as MSVC and ilk and manifest files, but these xmakes are not in charge. Users can control them freely with compiled parameters. These products usually do not need to be installed, but only implib is not only taken over by xmake but also needs to be installed. xmake not only takes over but also does not provide the user with an interface for output path. I think there is a problem.

I said before that xmake can never open an interface for these platforms and compiling unique temporary files. But it doesn't mean they can't be controlled. It's OK now. .

After_link., after_build you can do whatever you want through os.cp + rule, and you can copy it to the specified location.

And from my perspective, adding a function that supports the output path of implib is not a particularly troublesome job, and it can also make the special processing inside xmake more unified

implib at least mingw/ msvc and installation may be exported to the outside world. If you want to configure it through implibdir, it is OK, but don’t think about ilk exp and other messy files, it is impossible to add an interface separately.

@waruqi
Copy link
Member

waruqi commented Apr 19, 2025

我觉得最理想的情况是能让构建期和安装期有形式相同的 API,以 CMake 举例:

构建期:
set_target_properties(${PROJECT_NAME} PROPERTIES
PDB_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin # .pdb
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/bin # .exe, Unix Executable
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/lib # .so, .dylib
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/lib # .a, .lib, .dll.a

它也没有分离 implib 和 dll ,只是把 implib 跟随进了 archive 里面而已。

RUNTIME_OUTPUT_DIRECTORY 对应 binary target + set_targetdir
LIBRARY_OUTPUT_DIRECTORY 对应 shared target + set_targetdir
ARCHIVE_OUTPUT_DIRECTORY 对应 static target + set_targetdir

本质上都是相同的,仅仅只是 xmake 目前的设计是跟随 target kind ,而不是直接跟随 library kind,导致 implib 输出路径的部分差异而已。

也就是说 cmake 里面 implib 跟随了 archive 库路径,xmake 里面 implib 跟随了 shared target 的路径

就好比 你让 cmake 去分离 .a 和 .dll.a 的路径,一样分离不开。但是 xmake 里面就能分开。

毕竟不同构建工具设计不同,肯定有部分差异。

@SineStriker
Copy link
Contributor Author

SineStriker commented Apr 19, 2025

set_prefixdir 那个是通过设置 dll 路径,implib 也只是跟随 dll 的路径,并不能分离。效果 跟 set_targetdir 一样的,你 set_prefixdir 设置 dll 的路径,对应 implib 也会跟着修改调整。 并没有区别,效果完全一致。

不是啊,我用mingw和msvc都试过,动态库目标的.lib或者.dll.a都会安装到libdir指定的那个路径里去,而不是bindir的。

@Issues-translate-bot
Copy link

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


set_prefixdir The one is set by setting the dll path, implib only follows the dll path and cannot be separated. The effect is the same as set_targetdir. If you set_prefixdir set the path of the dll, the corresponding implib will also be modified and adjusted. There is no difference, the effect is exactly the same.
No, I have tried both mingw and msvc. The dynamic library target .lib or .dll.a will be installed in the path specified by libdir, instead of bindir.

@SineStriker
Copy link
Contributor Author

它也没有分离 implib 和 dll ,只是把 implib 跟随进了 archive 里面而已。

CMake 的做法是,我对一个 target 可以设置很多个 output directory,比如

  • RUNTIME_OUTPUT_DIRECTORY
  • LIBRARY_OUTPUT_DIRECTORY
  • ARCHIVE_OUTPUT_DIRECTORY
  • PDB_OUTPU_DIRECTORY

然后如果这个 target 的构建会输出多个文件,比如输出了.dll和.lib,那么.dll就会被输出到 RUNTIME_OUTPUT_DIRECTORY 里,而 LIBRARY_OUTPUT_DIRECTORY 被输出到 ARCHIVE_OUTPUT_DIRECTORY 里。
同理,CMake 可以给一个 target 的 install 设置很多个 destination,.dll 就会被安装到 RUNTIME 的那个 DESTINATION,.lib 就会被安装到 ARCHIVE 的那个 destination。

xmake 是一个 target 只能设置一个 output directory,就是那个 targetdir,不管这个 target 构建完了输出什么类型的文件都会被输出到 targetdir。
但是 xmake 里的一个 target 却可以设置多个 install destination(这点跟 CMake 差不多),可以设置 bindir 和 libdir,那么 dll、exe 就会被安装到 bindir 指定的路径,.lib、.dll.a 就会被安装到 libdir 指定的路径。(这个你可能是记错了,我用 MSVC 和 MinGW 都试过,安装的时候都是可以指定分离的,我觉得这就很不错)

就好比 你让 cmake 去分离 .a 和 .dll.a 的路径,一样分离不开。但是 xmake 里面就能分开。

这个你说得不对,是可以分离的。.a 是静态库目标才会输出的文件,.dll.a 是动态库目标输出的文件,它们根本不可能来自同一个目标,那我只要给这两个目标自己的 ARCHIVE_OUTPUT_DIRECTORY 设置成不一样的就行了。

add_library(static_target STATIC ...)
set_target_properties(static_target PROPERTIES
    ARCHIVE_OUTPUT_DIRECTORY dir1  # static_target 的 .a 就会被输出到 dir1 里
)

add_library(shared_target SHARED ...)
set_target_properties(shared_target PROPERTIES
    ARCHIVE_OUTPUT_DIRECTORY dir2  # shared_target 的 .dll.a 就会被输出到 dir2 里
)

如果你只能改全局的那个 CMAKE_ARCHIVE_OUTPUT_DIRECTORY,那当然没法把它们分离开,但是我可以单独设置每个目标的。

implib 至少 mingw/ msvc 以及安装上都也许会对外导出用到,想要通过 implibdir 配置,倒也可以,但是 ilk exp 以及其他乱七八糟的文件 就不要想了,不可能单独加接口的。

至于这些中间文件,要不要单独加接口,其实也不好说,我目前是只有一个 implib 的需求,但是以后会不会有其他人有其他文件的需求就不清楚了。反正 CMake 是给 MSVC 的 PDB 文件的输出路径也提供了一个接口,可能将来会有人提出需求,所以保留一定的扩展性会比较好。至于 ilk、exp 那些应该是真的没必要。

@SineStriker
Copy link
Contributor Author

目前是把set_targetdir定义成跟set_prefixdir相同形式的了。

如果set_targetdir只有一个参数,那么跟原来的没有任何区别,如

set_targetdir("$(buildir)")

如果set_targetdir后面跟着一个针对不同类型的输出定义的子目录 map,如

set_targetdir("$(buildir)", { bin = "bin", lib = "lib" })

那么,exe、dll 以及 unix 可执行文件会输出到$(buildir)/bin里去,lib、dll.a、so、dylib 会输出到$(buildir)/lib里去。

并且保留了一定的扩展性,以后如果要加别的类型的输出(比如 pdb,也不用增加新的 API,直接在上面扩展就行)。

如果我要取不同类型文件的输出路径或者目录,那么通过target:artifactdir或者target:artifactfile来获取即可。

@Issues-translate-bot
Copy link

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


Currently, set_targetdir is defined as the same form as set_prefixdir.

If set_targetdir has only one parameter, then there is no difference from the original one, such as

set_targetdir("$(buildir)")

If set_targetdir is followed by a subdirectory map defined for different types of outputs, such as

set_targetdir("$(buildir)", { bin = "bin", lib = "lib" })

Then, the exe, dll and unix executable files will be output to $(buildir)/bin, and lib, dll.a, so, dylib will be output to $(buildir)/lib.

It also retains a certain degree of expansion. If you want to add another type of output in the future (such as pdb, you don’t need to add a new API, just expand it directly).

If I want to take the output path or directory of a different type of file, then I can get it through target:artifactdir or target:artifactfile.

@Issues-translate-bot
Copy link

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


I just deleted the two implibs...

I think I just saw that Implibfile() is not here

It's OK, I'll add it back

@Issues-translate-bot
Copy link

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


So, you hope not to retain the artifactfile interface that maintains extensibility, but use the implibfile interface, right? What new output file should be added in the future and then the new interface?

This is the case now, because now the artifactfile and targetfile have functional intersections.

Unless you can think of a better extension interface name, you can only access various other products, but have no intersection with the targetfile. At least it will not be guaranteed to be mixed.

@Issues-translate-bot
Copy link

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


So, you hope not to retain the artifactfile interface that maintains extensibility, but use the implibfile interface, right? What new output file should be added in the future and then the new interface?

This is the case now, because now the artifactfile and targetfile have functional intersections.

Unless you can think of a better extension interface name, you can only access various other products, but have no intersection with targetfile. At least it will not be guaranteed to be mixed.

byproduct is OK, it's too long

@SineStriker
Copy link
Contributor Author

所以说,你是希望现在不要保留artifactfile这种保持扩展性的接口,而是用implibfile这个接口是吗?等到以后要加什么新的输出文件再加新的接口?

目前是这样的,因为现在这个 artifactfile 跟 targetfile 是有功能性交集的。

除非你能想个更好的扩展接口名,只能够访问 其他各种产物,但是跟 targetfile 没有任何交集。至少不会保证混用。

byproduct行不行

@Issues-translate-bot
Copy link

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


So, you hope not to retain the artifactfile interface that maintains extensibility, but use the implibfile interface, right? What new output file should be added in the future and then the new interface?

This is the case now, because now the artifactfile and targetfile have functional intersections.

Unless you can think of a better extension interface name, you can only access various other products, but have no intersection with targetfile. At least it will not be guaranteed to be mixed.

byproduct is OK

@waruqi
Copy link
Member

waruqi commented Apr 21, 2025

所以说,你是希望现在不要保留artifactfile这种保持扩展性的接口,而是用implibfile这个接口是吗?等到以后要加什么新的输出文件再加新的接口?

目前是这样的,因为现在这个 artifactfile 跟 targetfile 是有功能性交集的。
除非你能想个更好的扩展接口名,只能够访问 其他各种产物,但是跟 targetfile 没有任何交集。至少不会保证混用。

byproduct行不行

听上去怪怪的,还不如 artifactfile ,就按 artifactfile 来好了,内部限制下,仅仅处理额外的产物就行了

@Issues-translate-bot
Copy link

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


So, you hope not to retain the artifactfile interface that maintains extensibility, but use the implibfile interface, right? What new output file should be added in the future and then the new interface?

This is the case now, because now the artifactfile and targetfile have functional intersections.
Unless you can think of a better extension interface name, you can only access various other products, but have no intersection with targetfile. At least it will not be guaranteed to be mixed.

byproduct is OK

It sounds weird, it's better to use artifactfile. Just press artifactfile. Under internal restrictions, you can just process additional products.

@SineStriker
Copy link
Contributor Author

所以说,你是希望现在不要保留artifactfile这种保持扩展性的接口,而是用implibfile这个接口是吗?等到以后要加什么新的输出文件再加新的接口?

目前是这样的,因为现在这个 artifactfile 跟 targetfile 是有功能性交集的。
除非你能想个更好的扩展接口名,只能够访问 其他各种产物,但是跟 targetfile 没有任何交集。至少不会保证混用。

byproduct行不行

听上去怪怪的,还不如 artifactfile ,就按 artifactfile 来好了,内部限制下,仅仅处理额外的产物就行了

改成 artifactfile 了

@Issues-translate-bot
Copy link

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


So, you hope not to retain the artifactfile interface that maintains extensibility, but use the implibfile interface, right? What new output file should be added in the future and then the new interface?

This is the case now, because now the artifactfile and targetfile have functional intersections.
Unless you can think of a better extension interface name, you can only access various other products, but have no intersection with targetfile. At least it will not be guaranteed to be mixed.

byproduct is OK

It sounds weird, it's better to use artifactfile. Just press artifactfile. Under internal restrictions, you can just process additional products.

Change to artifactfile

if verbose then
-- show the full link command with raw arguments, it will expand @xxx.args for msvc/link on windows
print(linkinst:linkcmd(objectfiles, targetfile, {linkflags = linkflags, rawargs = true}))
print(linkinst:linkcmd(objectfiles, targetfile, {linkflags = linkflags, implib = target:artifactfile("implib"), rawargs = true}))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不要在这里设置。link 的 opt 里,原本就可以通过 target 取到 implib

opt.target = self:target()


if not dryrun then
assert(linkinst:link(objectfiles, targetfile, {linkflags = linkflags}))
assert(linkinst:link(objectfiles, targetfile, {linkflags = linkflags, implib = target:artifactfile("implib")}))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

还有这也去掉


-- ensure the implib directory
if opt and opt.implib then
os.mkdir(path.directory(opt.implib))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opt.target 里是可以取到的

opt.target = self:target()


-- add `-Wl,--out-implib,outputdir/libxxx.a` for xxx.dll on mingw/gcc
if targetkind == "shared" and self:is_plat("mingw") then
local implib = opt.implib or path.join(path.directory(targetfile), path.basename(targetfile) .. ".dll.a")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opt.target 也是可以取到的

-- add `-Wl,--out-implib,outputdir/libxxx.a` for xxx.dll on mingw/gcc
if targetkind == "shared" and self:is_plat("mingw") then
table.insert(flags_extra, "-Wl,--out-implib," .. path.join(path.directory(targetfile), path.basename(targetfile) .. ".dll.a"))
local implib = opt.implib or path.join(path.directory(targetfile), path.basename(targetfile) .. ".dll.a")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

变量名 统一改成 implibfile

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

然后直接从 opt.target 里面取

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opt.target 会很麻烦,因为可能这个 target 是个 package,还得判断 type

Copy link
Member

@waruqi waruqi Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

但目前只有这种方式,在 link_objects 这种通用逻辑的地方,不适合加 implib 而且,而且覆盖不全,很多地方 link 不走 link_objects

也就多判断个 type ,一两行代码的事情

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

行,那我改下

@SineStriker
Copy link
Contributor Author

@waruqi 不对,不能从 opt.target 里获取,我之前试过出问题了。比如说tests/projects/c++/shared_library_export_all这个项目,构建 bar 目标的时候,从 opt.target 里获取到的是 foo 目标。

具体我也搞不懂这是怎么回事,大概说来就是link_objects.lua的32行创建linkinst实例的时候传进去的是 foo 目标,但是在on_changed回调里的局部变量 target 又变成了 bar,但是 linkinst 的成员 target 还是 foo。

所以我也能理解了为什么linkargv那些函数明明 opt.target 明明存在了,还要把 targetfile、targetkind 当参数传进去,就是因为 opt.target 里的 target 有可能是错的。

@SineStriker
Copy link
Contributor Author

假如我在现在代码里的xmake/modules/core/tools/link.lualink函数里添加这一行:

print("TARGET: ", opt.target:name(), opt.implib or "/")

然后用 MSVC 编译的时候:

$ xmake build -v
[ 40%]: linking.release foo.dll
"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\link.exe" -dll -nologo -machine:x64 /opt:ref /opt:icf /def:build\.gens\foo\windows\x64\release\rules\symbols\export_all.def -out:build\windows\x64\release\foo.dll build\.objs\foo\windows\x64\release\src\foo.cpp.obj /IMPLIB:build\windows\x64\release\foo.lib
TARGET:  foo build\windows\x64\release\foo.lib
checking for the c compiler (cc) ... cl.exe
export: ?add@bar@@SAHHH@Z at src\bar.cpp
[ 58%]: linking.release bar.dll
"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\link.exe" -dll -nologo -machine:x64 /opt:ref /opt:icf /def:build\.gens\bar\windows\x64\release\rules\symbols\export_all.def -out:build\windows\x64\release\bar.dll build\.objs\bar\windows\x64\release\src\bar.cpp.obj /IMPLIB:build\windows\x64\release\bar.lib
TARGET:  foo build\windows\x64\release\bar.lib                    (这个地方出问题了)
[ 80%]: linking.release demo.exe
"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.43.34808\\bin\\HostX64\\x64\\link.exe" -nologo -dynamicbase -nxcompat -machine:x64 -libpath:build\windows\x64\release /opt:ref /opt:icf foo.lib bar.lib -out:build\windows\x64\release\demo.exe build\.objs\demo\windows\x64\release\src\main.cpp.obj
TARGET:  demo /
[100%]: build ok, spent 1.437s

@waruqi
Copy link
Member

waruqi commented Apr 22, 2025

所以我也能理解了为什么linkargv那些函数明明 opt.target 明明存在了,还要把 targetfile、targetkind 当参数传进去,就是因为 opt.target 里的 target 有可能是错的。

不是,那是因为这是早期的接口设计,当初还不支持传递 target,后来才追加的 target 到 opt

@waruqi
Copy link
Member

waruqi commented Apr 22, 2025

不对,不能从 opt.target 里获取,我之前试过出问题了。比如说tests/projects/c++/shared_library_export_all这个项目,构建 bar 目标的时候,从 opt.target 里获取到的是 foo 目标。

具体我也搞不懂这是怎么回事,大概说来就是link_objects.lua的32行创建linkinst实例的时候传进去的是 foo 目标,但是在on_changed回调里的局部变量 target 又变成了 bar,但是 linkinst 的成员 target 还是 foo。

按理不会。可能需要调下,或者部分改进下才行,先放着吧 回头我看下,link_objects 里肯定不能加的

@Issues-translate-bot
Copy link

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


So I can also understand why the linkargv functions clearly have opt.target exists, and they also have to pass targetfile and targetkind as parameters in it, because the target in opt.target may be wrong.

No, that's because this is an early interface design. It did not support passing target at the beginning, but later the target was added to opt

@Issues-translate-bot
Copy link

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


No, can't get it from opt.target. I've tried it before and there was a problem. For example, the tests/projects/c++/shared_library_export_all project, when building the bar target, the foo target is obtained from opt.target.

I don't understand what's going on. It's probably because the 32-line link_objects.lua is the foo target that is passed in when creating a linkinst instance, but the local variable target in the on_changed callback becomes bar again, but the target of the linkinst member is still foo.

It's not true. It may need to be adjusted or partially improved, let's leave it first. I'll look back and see, it's definitely not added in link_objects.

@waruqi waruqi mentioned this pull request Apr 23, 2025
@waruqi
Copy link
Member

waruqi commented Apr 23, 2025

@waruqi 不对,不能从 opt.target 里获取,我之前试过出问题了。比如说tests/projects/c++/shared_library_export_all这个项目,构建 bar 目标的时候,从 opt.target 里获取到的是 foo 目标。

具体我也搞不懂这是怎么回事,大概说来就是link_objects.lua的32行创建linkinst实例的时候传进去的是 foo 目标,但是在on_changed回调里的局部变量 target 又变成了 bar,但是 linkinst 的成员 target 还是 foo。

所以我也能理解了为什么linkargv那些函数明明 opt.target 明明存在了,还要把 targetfile、targetkind 当参数传进去,就是因为 opt.target 里的 target 有可能是错的。

这个问题我修复了,可以试下这个 patch #6355

后面也再那个 patch 上继续跟进

@waruqi waruqi closed this Apr 23, 2025
@waruqi
Copy link
Member

waruqi commented Apr 23, 2025

另外,建议开个 issue ,详细描述下当前支持的特性和改动点,以及配置支持的写法和 example ,方便后续更新到文档,以及其他用户查看和使用

@Issues-translate-bot
Copy link

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


In addition, it is recommended to open an issue to describe the currently supported features and changes points in detail, as well as the configuration supported writing methods and examples, so as to facilitate subsequent updates to documents, and to view and use by other users.

@SineStriker
Copy link
Contributor Author

另外,建议开个 issue ,详细描述下当前支持的特性和改动点,以及配置支持的写法和 example ,方便后续更新到文档,以及其他用户查看和使用

#6377

测试例子我之后有空去加。

@Issues-translate-bot
Copy link

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


In addition, it is recommended to open an issue to describe the currently supported features and changes points in detail, as well as the configuration supported writing methods and examples, so as to facilitate subsequent updates to documents, and to view and use by other users.

#6377

Test examples I have time to add.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants