Skip to content

Commit 4af9da7

Browse files
authored
Add support for recursive response files (#25390)
Fixes: #25387
1 parent ad53094 commit 4af9da7

File tree

4 files changed

+53
-38
lines changed

4 files changed

+53
-38
lines changed

test/common.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,6 +1328,10 @@ def parse_wasm(self, filename):
13281328
funcs.append(name)
13291329
return imports, exports, funcs
13301330

1331+
def output_name(self, basename):
1332+
suffix = get_output_suffix(self.get_cflags())
1333+
return basename + suffix
1334+
13311335
@classmethod
13321336
def setUpClass(cls):
13331337
super().setUpClass()

test/test_core.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4135,10 +4135,6 @@ def do_basic_dylink_test(self, **kwargs):
41354135
}
41364136
''', 'other says 11.', 'int sidey();', force_c=True, **kwargs)
41374137

4138-
def output_name(self, basename):
4139-
suffix = common.get_output_suffix(self.get_cflags())
4140-
return basename + suffix
4141-
41424138
@needs_dylink
41434139
@crossplatform
41444140
def test_dylink_basics(self):
@@ -7275,15 +7271,6 @@ def test(expected, args=None, assert_returncode=0):
72757271
test('|1|')
72767272
test('|1|', args=['-DDIRECT'])
72777273

7278-
def test_response_file(self):
7279-
out_js = self.output_name('response_file')
7280-
response_data = '-o "%s" "%s"' % (out_js, test_file('hello_world.cpp'))
7281-
create_file('rsp_file', response_data.replace('\\', '\\\\'))
7282-
self.run_process([EMCC, "@rsp_file"] + self.get_cflags())
7283-
self.do_run(out_js, 'hello, world', no_build=True)
7284-
7285-
self.assertContained('response file not found: foo.txt', self.expect_fail([EMCC, '@foo.txt']))
7286-
72877274
def test_linker_response_file(self):
72887275
objfile = 'response_file.o'
72897276
out_js = self.output_name('response_file')

test/test_other.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9424,8 +9424,8 @@ def test_emar_response_file(self):
94249424
create_file("file'2", ' ')
94259425
create_file("hyvää päivää", ' ')
94269426
create_file("snowman freezes covid ☃ 🦠", ' ')
9427-
rsp = response_file.create_response_file(("file'1", "file'2", "hyvää päivää", "snowman freezes covid ☃ 🦠"), shared.TEMP_DIR)
9428-
building.emar('cr', 'libfoo.a', ['@' + rsp])
9427+
create_file("tmp.rsp", response_file.create_response_file_contents(("file'1", "file'2", "hyvää päivää", "snowman freezes covid ☃ 🦠")))
9428+
building.emar('cr', 'libfoo.a', ['@tmp.rsp'])
94299429

94309430
def test_response_file_bom(self):
94319431
# Modern CMake version create response fils in UTF-8 but with BOM
@@ -13003,6 +13003,16 @@ def create_o(name, i):
1300313003
self.run_process(building.get_command_with_possible_response_file([EMCC, 'main.c'] + files))
1300413004
self.assertContained(str(count * (count - 1) // 2), self.run_js('a.out.js'))
1300513005

13006+
@crossplatform
13007+
def test_response_file(self):
13008+
out_js = self.output_name('response_file')
13009+
response_data = '-o "%s" "%s"' % (out_js, test_file('hello_world.cpp'))
13010+
create_file('rsp_file', response_data.replace('\\', '\\\\'))
13011+
self.run_process([EMCC, "@rsp_file"] + self.get_cflags())
13012+
self.do_run(out_js, 'hello, world', no_build=True)
13013+
13014+
self.assertContained('emcc: error: @foo.txt: No such file or directory', self.expect_fail([EMCC, '@foo.txt']))
13015+
1300613016
# Tests that the filename suffix of the response files can be used to detect which encoding the file is.
1300713017
@crossplatform
1300813018
def test_response_file_encoding(self):
@@ -13020,6 +13030,13 @@ def test_response_file_encoding(self):
1302013030
open('a.rsp', 'w', encoding=preferred_encoding).write('äö.c') # Write a response file using Python preferred encoding
1302113031
self.run_process([EMCC, '@a.rsp']) # ... and test that it is properly autodetected.
1302213032

13033+
@crossplatform
13034+
def test_response_file_recursive(self):
13035+
create_file('rsp2.txt', response_file.create_response_file_contents([test_file('hello_world.c'), '-o', 'hello.js']))
13036+
create_file('rsp1.txt', '@rsp2.txt\n')
13037+
self.run_process([EMCC, '@rsp1.txt'])
13038+
self.assertContained('hello, world!', self.run_js('hello.js'))
13039+
1302313040
def test_output_name_collision(self):
1302413041
# Ensure that the secondary filenames never collide with the primary output filename
1302513042
# In this case we explicitly ask for JS to be created in a file with the `.wasm` suffix.

tools/response_file.py

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,9 @@
1313
DEBUG = int(os.environ.get('EMCC_DEBUG', '0'))
1414

1515

16-
def create_response_file(args, directory):
17-
"""Routes the given cmdline param list in args into a new response file and
18-
returns the filename to it.
16+
def create_response_file_contents(args):
17+
"""Create response file contents based on list of arguments.
1918
"""
20-
21-
response_fd, response_filename = tempfile.mkstemp(prefix='emscripten_', suffix='.rsp.utf-8', dir=directory, text=True)
22-
23-
# Backslashes and other special chars need to be escaped in the response file.
2419
escape_chars = ['\\', '\"']
2520
# When calling llvm-ar on Linux and macOS, single quote characters ' should be escaped.
2621
if not WINDOWS:
@@ -40,6 +35,18 @@ def escape(arg):
4035
arg = '"%s"' % arg
4136
contents += arg + '\n'
4237

38+
return contents
39+
40+
41+
def create_response_file(args, directory):
42+
"""Routes the given cmdline param list in args into a new response file and
43+
returns the filename to it.
44+
"""
45+
# Backslashes and other special chars need to be escaped in the response file.
46+
contents = create_response_file_contents(args)
47+
48+
response_fd, response_filename = tempfile.mkstemp(prefix='emscripten_', suffix='.rsp.utf-8', dir=directory, text=True)
49+
4350
with os.fdopen(response_fd, 'w', encoding='utf-8') as f:
4451
f.write(contents)
4552

@@ -54,7 +61,7 @@ def escape(arg):
5461
return response_filename
5562

5663

57-
def read_response_file(response_filename):
64+
def expand_response_file(arg):
5865
"""Reads a response file, and returns the list of cmdline params found in the
5966
file.
6067
@@ -63,12 +70,19 @@ def read_response_file(response_filename):
6370
specified, first UTF-8 and then Python locale.getpreferredencoding() are
6471
attempted.
6572
66-
The parameter response_filename may start with '@'."""
67-
if response_filename.startswith('@'):
68-
response_filename = response_filename[1:]
73+
The parameter `arg` is the command line argument to be expanded."""
74+
75+
if arg.startswith('@'):
76+
response_filename = arg[1:]
77+
elif arg.startswith('-Wl,@'):
78+
response_filename = arg[5:]
79+
else:
80+
response_filename = None
6981

70-
if not os.path.exists(response_filename):
71-
raise OSError("response file not found: %s" % response_filename)
82+
# Is the argument is not a response file, or if the file does not exist
83+
# just return orginal argument.
84+
if not response_filename or not os.path.exists(response_filename):
85+
return [arg]
7286

7387
# Guess encoding based on the file suffix
7488
components = os.path.basename(response_filename).split('.')
@@ -97,20 +111,13 @@ def read_response_file(response_filename):
97111
if DEBUG:
98112
logging.warning(f'read response file {response_filename}: {args}')
99113

100-
return args
114+
# Response file can be recursive so call substitute_response_files on the arguments
115+
return substitute_response_files(args)
101116

102117

103118
def substitute_response_files(args):
104119
"""Substitute any response files found in args with their contents."""
105120
new_args = []
106121
for arg in args:
107-
if arg.startswith('@'):
108-
new_args += read_response_file(arg)
109-
elif arg.startswith('-Wl,@'):
110-
for a in read_response_file(arg[5:]):
111-
if a.startswith('-'):
112-
a = '-Wl,' + a
113-
new_args.append(a)
114-
else:
115-
new_args.append(arg)
122+
new_args += expand_response_file(arg)
116123
return new_args

0 commit comments

Comments
 (0)