Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/tools/response_file.py
6170 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
11
from . import shared
12
from .utils import WINDOWS
13
14
DEBUG = int(os.environ.get('EMCC_DEBUG', '0'))
15
16
17
def create_response_file_contents(args):
18
"""Create response file contents based on list of arguments.
19
"""
20
escape_chars = ['\\', '\"']
21
# When calling llvm-ar on Linux and macOS, single quote characters ' should be escaped.
22
if not WINDOWS:
23
escape_chars += ['\'']
24
25
def escape(arg):
26
for char in escape_chars:
27
arg = arg.replace(char, '\\' + char)
28
return arg
29
30
args = [escape(a) for a in args]
31
contents = ''
32
33
# Arguments containing spaces need to be quoted.
34
for arg in args:
35
if ' ' in arg:
36
arg = '"%s"' % arg
37
contents += arg + '\n'
38
39
return contents
40
41
42
def create_response_file(args, directory):
43
"""Routes the given cmdline param list in args into a new response file and
44
returns the filename to it.
45
"""
46
# Backslashes and other special chars need to be escaped in the response file.
47
contents = create_response_file_contents(args)
48
49
response_fd, response_filename = tempfile.mkstemp(prefix='emscripten_', suffix='.rsp.utf-8', dir=directory, text=True)
50
51
with os.fdopen(response_fd, 'w', encoding='utf-8') as f:
52
f.write(contents)
53
54
if DEBUG:
55
logging.warning(f'Creating response file {response_filename} with following contents: {contents}')
56
57
# Register the created .rsp file to be automatically cleaned up once this
58
# process finishes, so that caller does not have to remember to do it.
59
shared.get_temp_files().note(response_filename)
60
61
return response_filename
62
63
64
def expand_response_file(arg):
65
"""Reads a response file, and returns the list of cmdline params found in the
66
file.
67
68
The encoding that the response filename should be read with can be specified
69
as a suffix to the file, e.g. "foo.rsp.utf-8" or "foo.rsp.cp1252". If not
70
specified, first UTF-8 and then Python locale.getpreferredencoding() are
71
attempted.
72
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
81
82
# Is the argument is not a response file, or if the file does not exist
83
# just return original argument.
84
if not response_filename or not os.path.exists(response_filename):
85
return [arg]
86
87
# Guess encoding based on the file suffix
88
components = os.path.basename(response_filename).split('.')
89
encoding_suffix = components[-1].lower()
90
if len(components) > 1 and (encoding_suffix.startswith(('utf', 'cp', 'iso')) or encoding_suffix in {'ascii', 'latin-1'}):
91
guessed_encoding = encoding_suffix
92
else:
93
# On windows, recent version of CMake emit rsp files containing
94
# a BOM. Using 'utf-8-sig' works on files both with and without
95
# a BOM.
96
guessed_encoding = 'utf-8-sig'
97
98
try:
99
# First try with the guessed encoding
100
with open(response_filename, encoding=guessed_encoding) as f:
101
args = f.read()
102
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.
103
if DEBUG:
104
logging.warning(f'failed to parse response file {response_filename} with guessed encoding "{guessed_encoding}". Trying default system encoding...')
105
# If that fails, try with the Python default locale.getpreferredencoding()
106
with open(response_filename) as f:
107
args = f.read()
108
109
args = shlex.split(args)
110
111
if DEBUG:
112
logging.warning(f'read response file {response_filename}: {args}')
113
114
# Response file can be recursive so call substitute_response_files on the arguments
115
return substitute_response_files(args)
116
117
118
def substitute_response_files(args):
119
"""Substitute any response files found in args with their contents."""
120
new_args = []
121
for arg in args:
122
new_args += expand_response_file(arg)
123
return new_args
124
125