forked from godotengine/godot-mono-builds
-
Notifications
You must be signed in to change notification settings - Fork 0
/
os_utils.py
executable file
·226 lines (167 loc) · 7.46 KB
/
os_utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
import os
import os.path
from options import *
class BuildError(Exception):
'''Generic exception for custom build errors'''
def __init__(self, msg):
super(BuildError, self).__init__(msg)
self.message = msg
def run_command(command, args=[], cwd=None, env=None, name='command'):
def cmd_args_to_str(cmd_args):
return ' '.join([arg if not ' ' in arg else '"%s"' % arg for arg in cmd_args])
assert isinstance(command, str) and isinstance(args, list)
args = [command] + args
check_call_args = {}
if cwd is not None:
check_call_args['cwd'] = cwd
if env is not None:
check_call_args['env'] = env
import subprocess
try:
print('Running command \'%s\': %s' % (name, subprocess.list2cmdline(args)))
subprocess.check_call(args, **check_call_args)
print('Command \'%s\' completed successfully' % name)
except subprocess.CalledProcessError as e:
raise BuildError('\'%s\' exited with error code: %s' % (name, e.returncode))
print_env_sh_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'print_env.sh')
def source(script: str, cwd=None) -> dict:
popen_args = {}
if cwd is not None:
popen_args['cwd'] = cwd
import subprocess
cmd = 'bash -c \'source %s; bash %s\'' % (script, print_env_sh_path)
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, **popen_args)
output = proc.communicate()[0]
return dict(line.split('=', 1) for line in output.decode().split('\x00') if line)
# Creates the directory if no other file or directory with the same path exists
def mkdir_p(path):
if not os.path.exists(path):
print('creating directory: ' + path)
os.makedirs(path)
# Remove files and/or directories recursively
def rm_rf(*paths):
from shutil import rmtree
for path in paths:
if os.path.isfile(path):
print('removing file: ' + path)
os.remove(path)
elif os.path.isdir(path):
print('removing directory and its contents: ' + path)
rmtree(path)
ENV_PATH_SEP = ';' if os.name == 'nt' else ':'
def find_executable(name) -> str:
is_windows = os.name == 'nt'
windows_exts = os.environ['PATHEXT'].split(ENV_PATH_SEP) if is_windows else None
path_dirs = os.environ['PATH'].split(ENV_PATH_SEP)
search_dirs = path_dirs + [os.getcwd()] # cwd is last in the list
for dir in search_dirs:
path = os.path.join(dir, name)
if is_windows:
for extension in windows_exts:
path_with_ext = path + extension
if os.path.isfile(path_with_ext) and os.access(path_with_ext, os.X_OK):
return path_with_ext
else:
if os.path.isfile(path) and os.access(path, os.X_OK):
return path
return ''
def replace_in_new_file(src_file, search, replace, dst_file):
with open(src_file, 'r') as file:
content = file.read()
content = content.replace(search, replace)
with open(dst_file, 'w') as file:
file.write(content)
def replace_in_file(filepath, search, replace):
replace_in_new_file(src_file=filepath, search=search, replace=replace, dst_file=filepath)
def touch(filepath: str):
import pathlib
pathlib.Path(filepath).touch()
def get_emsdk_root():
# Shamelessly copied from Godot's detect.py
em_config_file = os.getenv('EM_CONFIG') or os.path.expanduser('~/.emscripten')
if not os.path.exists(em_config_file):
raise BuildError("Emscripten configuration file '%s' does not exist" % em_config_file)
with open(em_config_file) as f:
em_config = {}
try:
# Emscripten configuration file is a Python file with simple assignments.
exec(f.read(), em_config)
except StandardError as e:
raise BuildError("Emscripten configuration file '%s' is invalid:\n%s" % (em_config_file, e))
if 'BINARYEN_ROOT' in em_config and os.path.isdir(os.path.join(em_config.get('BINARYEN_ROOT'), 'emscripten')):
# New style, emscripten path as a subfolder of BINARYEN_ROOT
return os.path.join(em_config.get('BINARYEN_ROOT'), 'emscripten')
elif 'EMSCRIPTEN_ROOT' in em_config:
# Old style (but can be there as a result from previous activation, so do last)
return em_config.get('EMSCRIPTEN_ROOT')
else:
raise BuildError("'BINARYEN_ROOT' or 'EMSCRIPTEN_ROOT' missing in Emscripten configuration file '%s'" % em_config_file)
def globs(pathnames, dirpath='.'):
import glob
files = []
for pathname in pathnames:
files.extend(glob.glob(os.path.join(dirpath, pathname)))
return files
def xcrun_find_sdk(sdk_name):
import subprocess
xcrun_output = subprocess.check_output(['xcrun', '--sdk', sdk_name, '--show-sdk-path']).decode().strip()
if xcrun_output.startswith('xcrun: error: SDK "%s" cannot be located' % sdk_name):
return ''
sdk_path = xcrun_output
return sdk_path
def chmod_plus_x(file):
import os
import stat
umask = os.umask(0)
os.umask(umask)
st = os.stat(file)
os.chmod(file, st.st_mode | ((stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) & ~umask))
def get_clang_resource_dir(clang_command):
import shlex
from subprocess import check_output
return check_output(shlex.split(clang_command) + ['-print-resource-dir']).strip().decode('utf-8')
def try_find_libclang(toolchain_path: str = '', llvm_config=''):
import sys
from subprocess import check_output
hint_paths = []
if toolchain_path:
libclang = os.path.join(toolchain_path, 'usr', 'lib', 'libclang.dylib')
if os.path.isfile(libclang):
print('Found libclang at: \'%s\'' % libclang)
return libclang
if not llvm_config:
llvm_config = find_executable('llvm-config')
if not llvm_config:
print('WARNING: llvm-config not found')
return ''
elif not os.path.isfile(llvm_config):
raise RuntimeError('Specified llvm-config file not found: \'%s\'' % llvm_config)
llvm_libdir = check_output([llvm_config, '--libdir']).strip().decode('utf-8')
if llvm_libdir:
libsuffix = '.dylib' if sys.platform == 'darwin' else '.so'
hints = ['libclang', 'clang']
libclang = next((p for p in [os.path.join(llvm_libdir, h + libsuffix) for h in hints] if os.path.isfile(p)), '')
if libclang:
print('Found libclang at: \'%s\'' % libclang)
return libclang
return ''
def create_osxcross_wrapper(opts: RuntimeOpts, product: str, target: str, toolchain_path : str):
# OSXCROSS toolchain executables use rpath to locate the toolchain's shared libraries.
# However, when moving the toolchain without care, the rpaths can be broken.
# Since fixing the rpaths can be tedious, we use this wrapper to override LD_LIBRARY_PATH.
# The reason we don't just run configure and make with LD_LIBRARY_PATH is because
# we want the resulting configuration to be independent from out python scripts.
wrapper_src = """#!/bin/bash
OSXCROSS_COMMAND=$1;
shift;
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:%s";
${OSXCROSS_COMMAND} "$@";
exit $?;
""" % os.path.join(toolchain_path, 'lib')
build_dir = os.path.join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration))
wrapper_path = os.path.join(build_dir, 'osxcross_cmd_wrapper.sh')
mkdir_p(build_dir)
with open(wrapper_path, 'w') as f:
f.write(wrapper_src)
chmod_plus_x(wrapper_path)
return wrapper_path