xmake-io / xmake

🔥 A cross-platform build utility based on Lua
https://xmake.io
Apache License 2.0
10.14k stars 782 forks source link

多target的项目中,使用add_shflags("/NOENTRY")的纯资源动态库项目会对其它shared项目产生影响 #5360

Closed augustheart closed 4 months ago

augustheart commented 4 months ago

Xmake 版本

2.9.3

操作系统版本和架构

windows 11 23H2

描述问题

这个算是一个持续了两年的问题,之前的讨论中发现了这个问题,但是当时一来没分析到点子上,二来没几天工作就忙起来了(xmake相关只是我的业余爱好涉及),然后一拖就完全忘了这回事。直到这几天重新写了这个东西,然后发现了相同的问题,最后发现自己两年前就已经写过这东西,两年前就发现这个问题……废话不多说了,今天大概大脑转对了方向,终于发现了真正的问题所在: 最简单的项目: xmake.lua

target("res")
    set_kind("shared")
    add_shflags("/NOENTRY")
    add_files("src/*.rc")
target_end()

target("bugsample")
    set_kind("shared")
    add_files("src/main.cpp")
    add_syslinks("User32")
target_end()

main.cpp

// dllmain.cpp : Defines the entry point for the DLL application.
#include <windows.h>

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:{
        MessageBoxA(0,0,0,0);//这是为了链接user32
        break;
    }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

soui-sys-resource.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// Chinese (P.R.C.) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
#ifdef _WIN32
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#pragma code_page(936)
#endif //_WIN32

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\0"
END

#endif    // APSTUDIO_INVOKED

#endif    // Chinese (P.R.C.) resources
/////////////////////////////////////////////////////////////////////////////

resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by soui-sys-resource.rc
//

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        101
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

这就是一个最小的项目 然后:

xmake f -c
checking for platform ... windows
checking for architecture ... x64
checking for Microsoft Visual Studio (x64) version ... 2022
xmake clean
xmake
[ 50%]: compiling.release src\main.cpp
[ 50%]: compiling.release src\soui-sys-resource.rc
[ 62%]: linking.release res.dll
[ 87%]: linking.release bugsample.dll
error: main.cpp.obj : error LNK2019: 无法解析的外部符号 __imp_MessageBoxA,函数 DllMain 中引用了该符号
build\windows\x64\release\bugsample.dll : fatal error LNK1120: 1 个无法解析的外部命令

期待的结果

可以看到,上面两个项目之间互相没有依赖关系,应该互不影响才对。两个项目分别独立编译,也是没有问题的。

xmake clean
xmake b bugsample
checking for Microsoft Visual Studio (x64) version ... 2022
[ 50%]: compiling.release src\main.cpp
[ 75%]: linking.release bugsample.dll
[100%]: build ok, spent 0.641s
xmake b res
[ 75%]: linking.release res.dll
[100%]: build ok, spent 0.5s

但是,分别编译上面两个项目之后,紧接着xmake,炸了。

xmake
[ 75%]: linking.release bugsample.dll
error: main.cpp.obj : error LNK2019: 无法解析的外部符号 __imp_MessageBoxA,函数 DllMain 中引用了该符号
build\windows\x64\release\bugsample.dll : fatal error LNK1120: 1 个无法解析的外部命令

然后我再修改一下, 1.main.cpp复制一份,命名为main1.cpp,并且去掉MessageBox,这是为了不引用User32 2.target("res")中去掉add_shflags("/NOENTRY"),将main1.cpp加入编译

target("res")
    set_kind("shared")
    --add_shflags("/NOENTRY")
    add_files("src/*.rc","src/main1.cpp")
target_end()

target("bugsample")
    set_kind("shared")
    add_files("src/main.cpp")
    add_syslinks("User32")
target_end()

然后重新编译,结果一切正常。证明问题就在noentry这里

xmake clean
xmake f -c
checking for platform ... windows
checking for architecture ... x64
checking for Microsoft Visual Studio (x64) version ... 2022
xmake
[ 55%]: compiling.release src\main1.cpp
[ 55%]: compiling.release src\soui-sys-resource.rc
[ 55%]: compiling.release src\main.cpp
[ 66%]: linking.release res.dll
[ 88%]: linking.release bugsample.dll
[100%]: build ok, spent 1.047s

工程配置

--

附加信息和错误日志

然后是错误相关的详细日志

xmake f -c -vD
checking for platform ... windows
checking for architecture ... x64
checking for cl.exe ... C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\bin\HostX64\x64\cl.exe
checking for Microsoft Visual Studio (x64) version ... 2022
checking for zig ... ok
checkinfo: cannot runv(nim.exe --version), No such file or directory
checking for nim ... no
checkinfo: cannot runv(nim.exe --version), No such file or directory
checking for nim ... no
checking for link.exe ... C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\bin\HostX64\x64\link.exe
checking for the shared library linker (sh) ... link.exe
checking for link.exe ... C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\bin\HostX64\x64\link.exe
checking for the linker (ld) ... link.exe
configure
{
    plat = windows
    clean = true
    theme = default
    vs = 2022
    network = public
    ndk_stdcxx = true
    buildir = build
    ccache = true
    proxy_pac = pac.lua
    kind = static
    mode = release
    host = windows
    arch = x64
}
xmake -vD
[ 75%]: linking.release bugsample.dll
"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.40.33807\\bin\\HostX64\\x64\\link.exe" -dll -nologo -machine:x64 -out:build\windows\x64\release\bugsample.dll build\.objs\bugsample\windows\x64\release\src\main.cpp.obj
error: @programdir\core\main.lua:329: @programdir\actions\build\main.lua:148: @programdir\modules\async\runjobs.lua:325: @programdir\actions\build\kinds\shared.lua:53: @programdir\modules\core\tools\link.lua:175: main.cpp.obj : error LNK2019: 无法解析的外部符号 __imp_MessageBoxA,函数 DllMain 中引用了该符号
build\windows\x64\release\bugsample.dll : fatal error LNK1120: 1 个无法解析的外部命令

stack traceback:
    [C]: in function 'error'
    [@programdir\core\base\os.lua:973]:
    [@programdir\modules\core\tools\link.lua:175]: in function 'catch'
    [@programdir\core\sandbox\modules\try.lua:123]: in function 'try'
    [@programdir\modules\core\tools\link.lua:150]:
    [C]: in function 'xpcall'
    [@programdir\core\base\utils.lua:275]:
    [@programdir\core\tool\linker.lua:221]: in function 'link'
    [@programdir\actions\build\kinds\shared.lua:53]: in function 'callback'
    [@programdir\modules\core\project\depend.lua:217]: in function 'on_changed'
    [@programdir\actions\build\kinds\shared.lua:41]: in function '_do_link_target'
    [@programdir\actions\build\kinds\shared.lua:84]:
    [@programdir\actions\build\kinds\shared.lua:111]: in function '_link_target'
    [@programdir\actions\build\kinds\shared.lua:139]: in function 'jobfunc'
    [@programdir\modules\async\runjobs.lua:241]:
    [C]: in function 'xpcall'
    [@programdir\core\base\utils.lua:275]: in function 'trycall'
    [@programdir\core\sandbox\modules\try.lua:117]: in function 'try'
    [@programdir\modules\async\runjobs.lua:223]: in function 'cotask'
    [@programdir\core\base\scheduler.lua:406]:

stack traceback:
        [C]: in function 'error'
        @programdir\core\base\os.lua:973: in function 'base/os.raiselevel'
        (...tail calls...)
        @programdir\core\main.lua:329: in upvalue 'cotask'
        @programdir\core\base\scheduler.lua:406: in function <@programdir\core\base\scheduler.lua:399>

和两年前的结果一致,反正就是User32.lib没有传给link.exe

……
\link.exe" -dll -nologo -machine:x64 -out:build\windows\x64\release\bugsample.dll build\.objs\bugsample\windows\x64\release\src\main.cpp.obj
……

而单独编译时:

 xmake b -vD bugsample
[ 75%]: linking.release bugsample.dll
"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.40.33807\\bin\\HostX64\\x64\\link.exe" -dll -nologo -machine:x64 User32.lib -out:build\windows\x64\release\bugsample.dll build\.objs\bugsample\windows\x64\release\src\main.cpp.obj

非常稳定地重现了。

最后注意到我使用xmake b --help时

 -w, --warning                    Enable the warnings output. (deprecated)
        --files=FILES                Build the given source files.
                                     e.g.
                                         - xmake --files=src/main.c
                                         - xmake --files='src/*.c' [target]
                                         - xmake --files='src/**.c|excluded_file.c'
                                         - xmake --files='src/main.c;src/test.c'

          target                     The target name. It will build all default targets if this parameter is not
                                     specified.
                                         - bugsample
                                         - res

在我手动编译两个项目之后,xmake b -a还准备干啥?

waruqi commented 4 months ago

然后重新编译,结果一切正常。证明问题就在noentry这里

跟 noentry 没任何关系,是你新增的 "src/main1.cpp" 自动修复了问题,因为没任何 cpp files 的 rc shared target ,它不会被探测为 c++ language target,你显式加上 add_rules("c++") ,也能绕过这个问题

由于 rc target 没加 cpp files,它的 linker flags 没走 c++ linker 的加载逻辑,而 load linker 内部有缓存,跟另外一个 c++ target 的 linker flags 缓存冲突干扰了。。

我改进了下缓存的 key ,应该可以了。。这种情况只发生在纯 rc target 没任何 cpp files 的情况。

xmake update -s dev

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

augustheart commented 4 months ago

然后重新编译,结果一切正常。证明问题就在noentry这里

跟 noentry 没任何关系,是你新增的 "src/main1.cpp" 自动修复了问题,因为没任何 cpp files 的 rc shared target ,它不会被探测为 c++ language target,你显式加上 add_rules("c++") ,也能绕过这个问题

由于 rc target 没加 cpp files,它的 linker flags 没走 c++ linker 的加载逻辑,而 load linker 内部有缓存,跟另外一个 c++ target 的 linker flags 缓存冲突干扰了。。

我改进了下缓存的 key ,应该可以了。。这种情况只发生在纯 rc target 没任何 cpp files 的情况。

xmake update -s dev

5361

实测已经修正

Issues-translate-bot commented 4 months ago

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


Then recompile and everything works fine. Prove that the problem lies here in noentry

It has nothing to do with noentry. Your newly added "src/main1.cpp" automatically fixes the problem. Because there is no rc shared target for cpp files, it will not be detected as a c++ language target. You have to add it explicitly. Using add_rules("c++") can also circumvent this problem.

Since the rc target does not add cpp files, its linker flags do not use the loading logic of the c++ linker, and the load linker has an internal cache, which conflicts with the linker flags cache of another c++ target. .

I improved the cache key and it should be fine. . This only happens with pure rc targets without any cpp files.

xmake update -s dev

5361

The actual measurement has been corrected