链接命令行过长:在SCons中进行链接时如何使用响应文件来解决Windows上的问题

3

其他人一样,我有一个超过Windows cmd行限制的链接行。对于大多数情况,我们通过使用子集的目标文件构建中间存档文件(也称为静态库),并使用这些存档文件执行最终链接来解决了这个问题。然而,如果在Google Test中使用此策略,则会导致找不到测试,特别是存档的目标文件中定义的测试。

更新:这就是原因。我可能会使用这个解决方法,但我仍然想知道如何在scons下使用响应文件。

LongCmdLinesOnWin32修复存在问题。我们有一个包含空格的cygwin环境和路径名,因此一些编译器的绝对路径需要加引号。LongCmdLinesOnWin32脚本首先需要扩展以处理嵌入式引号和空格(否则它会创建单个路径名的分离标记)。更严重的是,在使用MS Visual Studio时,编译器命令只是'cl',即不包括路径名。这在PATH环境中不可用--它似乎是动态设置的(某种方式)并且在构造LongCmdLinesOnWin32脚本的cmdline参数时不可见。但我离题了....

似乎有一个更简单(并且对我来说合适)的解决方案:响应文件,也被gcc支持

我编写了一个小函数将对象名称列表打印到文本文件中,每行一个,类似于:

"""
  In place for generating response files
"""
def gen_response_file(filename,file_list):
    with open(filename,"w") as f:
        for obj_name in file_list:
            f.write ('%s\n' %os.path.abspath(str(obj_name)).replace('\\','/'))
    return filename

然后我尝试在文件名前添加“@”字符,并将其添加到选项列表中。

命令行显示的内容如下:

link /nologo /MACHINE:x86 /DEBUG @E:\dev\pcoip_view_client\soft_test.rsp /OUT:blah_client\blah_client_tests.exe /LIBPATH:\\sterbkp03\qt\4.8.2\lib ....

如果我只是将文件命名为“soft_test”,那么scons会添加后缀“.obj”,链接器找不到它,因此我尝试添加后缀“.rsp”。现在,链接器抱怨找不到文件,但实际上该文件存在。我从scons中捕获了输出并将其粘贴到了一个批处理文件中。当我从VS 2008命令行环境中运行批处理文件时,链接就像魔术般地工作了,所以看起来scons在某种程度上导致了找不到文件的问题。
我尝试更改路径,使用绝对路径(@C:\blah\soft_test.rsp)、相对路径(@.\soft_test.rsp)和只使用@soft_test.rsp,但它们都没有起作用。
LINK : fatal error LNK1104: cannot open file '@E:\dev\swift.dev\blah_client\soft_test.rsp'
scons: *** [blah_client\blah_client_tests.exe] Error 1104

我是在Windows 7-64系统下使用scons v2.1.0.r5357、VS 2008和Python 2.7。我的scons文件如下:

test_objects = tenv.Object(test_sources)
xx = gen_response_file('soft_test.rsp',test_objects)
tenv.Append( LINKFLAGS = [ '@%s' % os.path.abspath(xx)]) # 
test_exe = tenv.Program(target = 'blah_client_tests', source = objects + moc_objects + qrc_objects )

任何建议都非常感谢。
更新:我尝试了使用gcc并没有问题。我的猜测是,与Visual Studio工具相关联的scons规则有足够不同的地方,会引起困扰。

请提供SCons的完整输出。 - Brady
嗯,它很庞大,我认为它并没有比我上面提供的片段更具信息性——只是有一个很长的对象名称列表。 - TheDuke
1个回答

1
我尝试在Linux下使用gcc来复现这个问题,但是遇到了一个不同的问题,解决方案可能会有所帮助。
最初,我使用了这个SConscript文件:
import os

"""
  In place for generating response files
"""
def gen_response_file(filename,file_list):
    with open(filename,"w") as f:
        for obj_name in file_list:
            f.write ('%s\n' %os.path.abspath(str(obj_name)).replace('\\','/'))
    return filename

env = Environment()

test_objects = env.Object(target = 'testClass', source = 'testClass.cc')

resp_file = gen_response_file('response_file.rsp', test_objects)

env.Append(LINKFLAGS = [ '@%s' % os.path.abspath(resp_file)])
env.Program(target = 'helloWorld', source = 'helloWorld.cc')

这是我使用的相关源文件:

# tree .
.
|-- SConstruct
|-- helloWorld.cc
|-- testClass.cc
`-- testClass.h

这里的主程序是 helloWorld.cc,它包含了 testClass.h 并链接了 testClass.o。当我尝试编译时,响应文件被正确生成(只包含 /some/path/testClass.o),并被编译器读取。但问题在于,testClass.o 没有被编译,因为 SCons 似乎没有识别响应文件中列出的对象之间的依赖关系。以下是结果:

# scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o helloWorld.o -c helloWorld.cc
g++ -o helloWorld @/some/path/response_file.rsp helloWorld.o
g++: /some/path/testClass.o: No such file or directory
scons: *** [helloWorld] Error 1
scons: building terminated because of errors.

这似乎是SCons的一个失败,因为它没有分析响应文件。为了解决这个问题,我不得不使用Depends()函数,如下摘录所示:
...
bin = env.Program(target = 'helloWorld', source = 'helloWorld.cc')
env.Depends(bin, test_objects)

这段话的意思是:“这个方法可行,并给了我以下结果:”。其中的HTML代码保留不变,不需要解释。
# scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o helloWorld.o -c helloWorld.cc
g++ -o testClass.o -c testClass.cc
g++ -o helloWorld @/some/path/response_file.rsp helloWorld.o
scons: done building targets.

我知道这并没有回答关于为什么找不到响应文件的原始问题,但一旦你解决了这个问题,你很可能会遇到上述提到的问题,并且必须使用Depends()函数。

是的,我也遇到了这个问题,但我用不同的方法解决了它——抱歉,应该提一下。我没有直接将源文件作为响应文件的输入,而是首先生成了一个对象列表(如 obj_list = env.Object(src_list))。env.Object 暴露了依赖关系给 scons 并确保它们被生成。 - TheDuke
有趣的是:gcc似乎没有问题。 - TheDuke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接