Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/tools/response_file.py
4128 views
1
# Copyright 2013 The Emscripten Authors. All rights reserved.
2
# Emscripten is available under two separate licenses, the MIT license and the
3
# University of Illinois/NCSA Open Source License. Both these licenses can be
4
# found in the LICENSE file.
5
6
import logging
7
import os
8
import shlex
9
import tempfile
10
from .utils import WINDOWS
11
12
13
DEBUG = int(os.environ.get('EMCC_DEBUG', '0'))
14
15
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.
19
"""
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.
24
escape_chars = ['\\', '\"']
25
# When calling llvm-ar on Linux and macOS, single quote characters ' should be escaped.
26
if not WINDOWS:
27
escape_chars += ['\'']
28
29
def escape(arg):
30
for char in escape_chars:
31
arg = arg.replace(char, '\\' + char)
32
return arg
33
34
args = [escape(a) for a in args]
35
contents = ''
36
37
# Arguments containing spaces need to be quoted.
38
for arg in args:
39
if ' ' in arg:
40
arg = '"%s"' % arg
41
contents += arg + '\n'
42
43
with os.fdopen(response_fd, 'w', encoding='utf-8') as f:
44
f.write(contents)
45
46
if DEBUG:
47
logging.warning(f'Creating response file {response_filename} with following contents: {contents}')
48
49
# Register the created .rsp file to be automatically cleaned up once this
50
# process finishes, so that caller does not have to remember to do it.
51
from . import shared
52
shared.get_temp_files().note(response_filename)
53
54
return response_filename
55
56
57
def read_response_file(response_filename):
58
"""Reads a response file, and returns the list of cmdline params found in the
59
file.
60
61
The encoding that the response filename should be read with can be specified
62
as a suffix to the file, e.g. "foo.rsp.utf-8" or "foo.rsp.cp1252". If not
63
specified, first UTF-8 and then Python locale.getpreferredencoding() are
64
attempted.
65
66
The parameter response_filename may start with '@'."""
67
if response_filename.startswith('@'):
68
response_filename = response_filename[1:]
69
70
if not os.path.exists(response_filename):
71
raise OSError("response file not found: %s" % response_filename)
72
73
# Guess encoding based on the file suffix
74
components = os.path.basename(response_filename).split('.')
75
encoding_suffix = components[-1].lower()
76
if len(components) > 1 and (encoding_suffix.startswith(('utf', 'cp', 'iso')) or encoding_suffix in {'ascii', 'latin-1'}):
77
guessed_encoding = encoding_suffix
78
else:
79
# On windows, recent version of CMake emit rsp files containing
80
# a BOM. Using 'utf-8-sig' works on files both with and without
81
# a BOM.
82
guessed_encoding = 'utf-8-sig'
83
84
try:
85
# First try with the guessed encoding
86
with open(response_filename, encoding=guessed_encoding) as f:
87
args = f.read()
88
except (ValueError, LookupError): # UnicodeDecodeError is a subclass of ValueError, and Python raises either a ValueError or a UnicodeDecodeError on decode errors. LookupError is raised if guessed encoding is not an encoding.
89
if DEBUG:
90
logging.warning(f'failed to parse response file {response_filename} with guessed encoding "{guessed_encoding}". Trying default system encoding...')
91
# If that fails, try with the Python default locale.getpreferredencoding()
92
with open(response_filename) as f:
93
args = f.read()
94
95
args = shlex.split(args)
96
97
if DEBUG:
98
logging.warning(f'read response file {response_filename}: {args}')
99
100
return args
101
102
103
def substitute_response_files(args):
104
"""Substitute any response files found in args with their contents."""
105
new_args = []
106
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)
116
return new_args
117
118