2071 lines
87 KiB
Python
Executable File
2071 lines
87 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
from __future__ import print_function
|
|
import sys, optparse, subprocess, os, os.path, errno, zipfile, string, json, platform, shutil, tarfile, tempfile, multiprocessing, re, stat, copy
|
|
if sys.version_info >= (3,):
|
|
from urllib.parse import urljoin
|
|
from urllib.request import urlopen
|
|
from urllib.error import HTTPError
|
|
else:
|
|
from urlparse import urljoin
|
|
from urllib2 import urlopen, HTTPError
|
|
|
|
# EMSDK_DEV is a developer mode flag, which, if true, the SDK is downloaded from a 'staging' online source,
|
|
# instead of the public source. New releases are first deployed to the staging source for testing, before
|
|
# being published to the public. Don't enable this unless you develop EMSDK itself and need to access the
|
|
# staging source repository instead.
|
|
EMSDK_DEV = bool(os.getenv('EMSDK_DEV')) if os.getenv('EMSDK_DEV') != None else False
|
|
|
|
if EMSDK_DEV:
|
|
print('EMSDK_DEV active.')
|
|
emsdk_master_server = 'http://clb.demon.fi/emscripten_dev/'
|
|
else:
|
|
emsdk_master_server = 'https://s3.amazonaws.com/mozilla-games/emscripten/'
|
|
|
|
emsdk_packages_url = urljoin(emsdk_master_server, 'packages/')
|
|
|
|
emscripten_git_repo = 'https://github.com/kripken/emscripten/'
|
|
|
|
# Enable this to do very verbose printing about the different steps that are being run. Useful for debugging.
|
|
VERBOSE = bool(os.getenv('EMSDK_VERBOSE')) if os.getenv('EMSDK_VERBOSE') != None else False
|
|
|
|
WINDOWS = False
|
|
if os.name == 'nt':
|
|
WINDOWS = True
|
|
ENVPATH_SEPARATOR = ';'
|
|
|
|
LINUX = False
|
|
if platform.system() == 'Linux':
|
|
LINUX = True
|
|
ENVPATH_SEPARATOR = ':'
|
|
|
|
OSX = False
|
|
if platform.mac_ver()[0] != '':
|
|
OSX = True
|
|
ENVPATH_SEPARATOR = ':'
|
|
|
|
CPU_CORES = max(multiprocessing.cpu_count()-1, 1) # Don't saturate all cores to not steal the whole system, but be aggressive.
|
|
|
|
CMAKE_BUILD_TYPE_OVERRIDE = None
|
|
|
|
# If true, perform a --shallow clone of git.
|
|
GIT_CLONE_SHALLOW = False
|
|
|
|
# If true, LLVM backend is built with tests enabled, and Binaryen is built with Visual Studio static analyzer enabled.
|
|
BUILD_FOR_TESTING = False
|
|
|
|
# If 'auto', assertions are decided by the build type (Release&MinSizeRel=disabled, Debug&RelWithDebInfo=enabled)
|
|
# Other valid values are 'ON' and 'OFF'
|
|
ENABLE_LLVM_ASSERTIONS = 'auto'
|
|
|
|
def to_unix_path(p):
|
|
return p.replace('\\', '/')
|
|
|
|
def emsdk_path(): return to_unix_path(os.path.dirname(os.path.realpath(__file__)))
|
|
|
|
emscripten_config_directory = os.path.expanduser("~/")
|
|
# If .emscripten exists, we are configuring as embedded inside the emsdk directory.
|
|
if os.path.exists(os.path.join(emsdk_path(), '.emscripten')):
|
|
emscripten_config_directory = emsdk_path()
|
|
|
|
EMSDK_SET_ENV = 'emsdk_set_env.bat' if WINDOWS else 'emsdk_set_env.sh'
|
|
|
|
# Finds the given executable 'program' in PATH. Operates like the Unix tool 'which'.
|
|
def which(program):
|
|
def is_exe(fpath):
|
|
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
|
|
|
|
fpath, fname = os.path.split(program)
|
|
if fpath:
|
|
if is_exe(program):
|
|
return program
|
|
else:
|
|
for path in os.environ["PATH"].split(os.pathsep):
|
|
path = path.strip('"')
|
|
exe_file = os.path.join(path, program)
|
|
if is_exe(exe_file):
|
|
return exe_file
|
|
|
|
if WINDOWS and not '.' in fname:
|
|
if is_exe(exe_file + '.exe'):
|
|
return exe_file + '.exe'
|
|
if is_exe(exe_file + '.cmd'):
|
|
return exe_file + '.cmd'
|
|
if is_exe(exe_file + '.bat'):
|
|
return exe_file + '.bat'
|
|
|
|
return None
|
|
|
|
CMAKE_GENERATOR = 'Unix Makefiles'
|
|
if WINDOWS:
|
|
# Detect which CMake generator to use when building on Windows
|
|
if '--mingw' in sys.argv: CMAKE_GENERATOR = 'MinGW Makefiles'
|
|
elif '--vs2013' in sys.argv: CMAKE_GENERATOR = 'Visual Studio 12'
|
|
elif '--vs2015' in sys.argv: CMAKE_GENERATOR = 'Visual Studio 14'
|
|
else:
|
|
vs2015_exists = 'VS140COMNTOOLS' in os.environ or 'VSSDK140Install' in os.environ or os.path.isdir(os.path.join(os.environ['ProgramFiles'], 'Microsoft Visual Studio 14.0')) or ('ProgramFiles(x86)' in os.environ and os.path.isdir(os.path.join(os.environ['ProgramFiles(x86)'], 'Microsoft Visual Studio 14.0')))
|
|
vs2013_exists = 'VS120COMNTOOLS' in os.environ or os.path.isdir(os.path.join(os.environ['ProgramFiles'], 'Microsoft Visual Studio 12.0')) or ('ProgramFiles(x86)' in os.environ and os.path.isdir(os.path.join(os.environ['ProgramFiles(x86)'], 'Microsoft Visual Studio 12.0')))
|
|
mingw_exists = which('mingw32-make') != None and which('g++') != None
|
|
if vs2015_exists: CMAKE_GENERATOR = 'Visual Studio 14'
|
|
elif mingw_exists: CMAKE_GENERATOR = 'MinGW Makefiles'
|
|
elif vs2013_exists: CMAKE_GENERATOR = 'Visual Studio 12' # VS2013 is no longer supported, so attempt it as a last resort if someone might want to insist using it.
|
|
else: CMAKE_GENERATOR = '' # No detected generator
|
|
if VERBOSE: print('CMAKE_GENERATOR: ' + CMAKE_GENERATOR)
|
|
sys.argv = filter(lambda x: x not in ['--mingw', '--vs2013', '--vs2015'], sys.argv)
|
|
|
|
# Computes a suitable path prefix to use when building with a given generator.
|
|
def cmake_generator_prefix():
|
|
if CMAKE_GENERATOR == 'Visual Studio 14': return '_vs2015'
|
|
elif CMAKE_GENERATOR == 'MinGW Makefiles': return '_mingw'
|
|
return '' # Unix Makefiles and Visual Studio 2013 do not specify a path prefix for backwards path compatibility
|
|
|
|
# Removes a directory tree even if it was readonly, and doesn't throw exception on failure.
|
|
def remove_tree(d):
|
|
if VERBOSE: print('remove_tree(' + str(d) + ')')
|
|
try:
|
|
def remove_readonly_and_try_again(func, path, exc_info):
|
|
if not (os.stat(path).st_mode & stat.S_IWRITE):
|
|
os.chmod(path, stat.S_IWRITE)
|
|
func(path)
|
|
else:
|
|
raise
|
|
shutil.rmtree(d, onerror=remove_readonly_and_try_again)
|
|
except Exception as e:
|
|
if VERBOSE: print('remove_tree threw an exception, ignoring: ' + str(e))
|
|
pass
|
|
|
|
def win_set_environment_variable_direct(key, value, system=True):
|
|
prev_path = os.environ['PATH']
|
|
try:
|
|
py = find_used_python()
|
|
if py:
|
|
py_path = to_native_path(py.expand_vars(py.activated_path))
|
|
os.environ['PATH'] = os.environ['PATH'] + ';' + py_path
|
|
import win32api, win32con
|
|
if system: # Read globally from ALL USERS section.
|
|
folder = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', 0, win32con.KEY_ALL_ACCESS)
|
|
else: # Register locally from CURRENT USER section.
|
|
folder = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, 'Environment', 0, win32con.KEY_ALL_ACCESS)
|
|
win32api.RegSetValueEx(folder, key, 0, win32con.REG_EXPAND_SZ, value)
|
|
if VERBOSE: print('Set key=' + key + ' with value ' + value + ' in registry.')
|
|
except Exception as e:
|
|
if e[0] == 5 or e[2] == 'Access is denied.':
|
|
print('Error! Failed to set the environment variable \'' + key + '\'! Setting environment variables permanently requires administrator access. Please rerun this command with administrative privileges. This can be done for example by holding down the Ctrl and Shift keys while opening a command prompt in start menu.')
|
|
sys.exit(1)
|
|
print('Failed to write environment variable ' + key + ':', file=sys.stderr)
|
|
print(str(e), file=sys.stderr)
|
|
win32api.RegCloseKey(folder)
|
|
os.environ['PATH'] = prev_path
|
|
return None
|
|
|
|
win32api.RegCloseKey(folder)
|
|
os.environ['PATH'] = prev_path
|
|
win32api.SendMessage(win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment')
|
|
|
|
def win_get_environment_variable(key, system=True):
|
|
prev_path = os.environ['PATH']
|
|
try:
|
|
py = find_used_python()
|
|
if py:
|
|
py_path = to_native_path(py.expand_vars(py.activated_path))
|
|
os.environ['PATH'] = os.environ['PATH'] + ';' + py_path
|
|
import win32api, win32con
|
|
if system: # Read globally from ALL USERS section.
|
|
folder = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment')
|
|
else: # Register locally from CURRENT USER section.
|
|
folder = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, 'Environment')
|
|
value = str(win32api.RegQueryValueEx(folder, key)[0])
|
|
except Exception as e:
|
|
if e[0] != 2: # 'The system cannot find the file specified.'
|
|
print('Failed to read environment variable ' + key + ':', file=sys.stderr)
|
|
print(str(e), file=sys.stderr)
|
|
win32api.RegCloseKey(folder)
|
|
os.environ['PATH'] = prev_path
|
|
return None
|
|
win32api.RegCloseKey(folder)
|
|
os.environ['PATH'] = prev_path
|
|
return value
|
|
|
|
def win_environment_variable_exists(key, system=True):
|
|
value = win_get_environment_variable(key, system)
|
|
return value != None and len(value) > 0
|
|
|
|
def win_get_active_environment_variable(key):
|
|
value = win_get_environment_variable(key, False)
|
|
if value != None:
|
|
return value
|
|
return win_get_environment_variable(key, True)
|
|
|
|
def win_set_environment_variable(key, value, system=True):
|
|
if VERBOSE: print('set ' + str(key) + '=' + str(value) + ', in system=' + str(system), file=sys.stderr)
|
|
previous_value = win_get_environment_variable(key, system)
|
|
if previous_value == value:
|
|
if VERBOSE: print(' no need to set, since same value already exists.')
|
|
return # No need to elevate UAC for nothing to set the same value, skip.
|
|
|
|
if not value:
|
|
try:
|
|
if system:
|
|
cmd = ['REG', 'DELETE', 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', '/V', key, '/f']
|
|
else:
|
|
cmd = ['REG', 'DELETE', 'HKCU\\Environment', '/V', key, '/f']
|
|
if VERBOSE: print(str(cmd))
|
|
value = subprocess.call(cmd, stdout=subprocess.PIPE)
|
|
except Exception as e:
|
|
return
|
|
return
|
|
|
|
try:
|
|
if system:
|
|
win_set_environment_variable_direct(key, value, system)
|
|
return
|
|
value = value.replace('%', '^%') # Escape % signs so that we don't expand references to environment variables.
|
|
if len(value) >= 1024:
|
|
print('ERROR! The new environment variable ' + key + ' is more than 1024 characters long! A value this long cannot be set via command line: please add the environment variable specified above to system environment manually via Control Panel.', file=sys.stderr)
|
|
sys.exit(1)
|
|
cmd = ['SETX', key, value]
|
|
if VERBOSE: print(str(cmd))
|
|
retcode = subprocess.call(cmd, stdout=subprocess.PIPE)
|
|
if retcode is not 0:
|
|
print('ERROR! Failed to set environment variable ' + key + '=' + value + '. You may need to set it manually.', file=sys.stderr)
|
|
except Exception as e:
|
|
print('ERROR! Failed to set environment variable ' + key + '=' + value + ':', file=sys.stderr)
|
|
print(str(e), file=sys.stderr)
|
|
print('You may need to set it manually.', file=sys.stderr)
|
|
|
|
def win_delete_environment_variable(key, system=True):
|
|
if VERBOSE: print('win_delete_environment_variable(key=' + key + ', system=' + str(system) + ')')
|
|
win_set_environment_variable(key, None, system)
|
|
|
|
# Returns the absolute pathname to the given path inside the Emscripten SDK.
|
|
def sdk_path(path):
|
|
if os.path.isabs(path):
|
|
return path
|
|
else:
|
|
return to_unix_path(os.path.join(os.path.dirname(os.path.realpath(__file__)), path))
|
|
|
|
# Modifies the given file in-place to contain '\r\n' line endings.
|
|
def file_to_crlf(filename):
|
|
text = open(filename, 'r').read()
|
|
text = text.replace('\r\n', '\n').replace('\n', '\r\n')
|
|
open(filename, 'wb').write(text)
|
|
|
|
# Modifies the given file in-place to contain '\n' line endings.
|
|
def file_to_lf(filename):
|
|
text = open(filename, 'r').read()
|
|
text = text.replace('\r\n', '\n')
|
|
open(filename, 'wb').write(text)
|
|
|
|
# Removes a single file, suppressing exceptions on failure.
|
|
def rmfile(filename):
|
|
if VERBOSE: print('rmfile(' + filename + ')')
|
|
try:
|
|
os.remove(filename)
|
|
except:
|
|
pass
|
|
|
|
def fix_lineendings(filename):
|
|
if WINDOWS:
|
|
file_to_crlf(filename)
|
|
else:
|
|
file_to_lf(filename)
|
|
|
|
# http://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python
|
|
def mkdir_p(path):
|
|
if VERBOSE: print('mkdir_p(' + path + ')')
|
|
if os.path.exists(path):
|
|
return
|
|
try:
|
|
os.makedirs(path)
|
|
except OSError as exc: # Python >2.5
|
|
if exc.errno == errno.EEXIST and os.path.isdir(path):
|
|
pass
|
|
else: raise
|
|
|
|
def num_files_in_directory(path):
|
|
if not os.path.isdir(path):
|
|
return 0
|
|
return len([name for name in os.listdir(path) if os.path.exists(os.path.join(path, name))])
|
|
|
|
def run(cmd, cwd=None):
|
|
if VERBOSE: print('run(cmd=' + str(cmd) + ', cwd=' + str(cwd) + ')')
|
|
process = subprocess.Popen(cmd, cwd=cwd, env=os.environ.copy())
|
|
process.communicate()
|
|
if process.returncode != 0:
|
|
print(str(cmd) + ' failed with error code ' + str(process.returncode) + '!')
|
|
return process.returncode
|
|
|
|
# http://pythonicprose.blogspot.fi/2009/10/python-extract-targz-archive.html
|
|
def untargz(source_filename, dest_dir, unpack_even_if_exists=False):
|
|
if VERBOSE: print('untargz(source_filename=' + source_filename + ', dest_dir=' + dest_dir + ')')
|
|
if not unpack_even_if_exists and num_files_in_directory(dest_dir) > 0:
|
|
print("File '" + source_filename + "' has already been unpacked, skipping.")
|
|
return True
|
|
print("Unpacking '" + source_filename + "' to '" + dest_dir + "'")
|
|
mkdir_p(dest_dir)
|
|
run(['tar', '-xvf', sdk_path(source_filename), '--strip', '1'], cwd=dest_dir)
|
|
#tfile = tarfile.open(source_filename, 'r:gz')
|
|
#tfile.extractall(dest_dir)
|
|
return True
|
|
|
|
# http://stackoverflow.com/questions/12886768/simple-way-to-unzip-file-in-python-on-all-oses
|
|
def unzip(source_filename, dest_dir, unpack_even_if_exists=False):
|
|
if VERBOSE: print('unzip(source_filename=' + source_filename + ', dest_dir=' + dest_dir + ')')
|
|
if not unpack_even_if_exists and num_files_in_directory(dest_dir) > 0:
|
|
print("File '" + source_filename + "' has already been unpacked, skipping.")
|
|
return True
|
|
print("Unpacking '" + source_filename + "' to '" + dest_dir + "'")
|
|
mkdir_p(dest_dir)
|
|
common_subdir = None
|
|
try:
|
|
with zipfile.ZipFile(source_filename) as zf:
|
|
# Implement '--strip 1' behavior to unzipping by testing if all the files in the zip reside in a common subdirectory, and if so,
|
|
# we move the output tree at the end of uncompression step.
|
|
for member in zf.infolist():
|
|
words = member.filename.split('/')
|
|
if len(words) > 1: # If there is a directory component?
|
|
if common_subdir == None:
|
|
common_subdir = words[0]
|
|
elif common_subdir != words[0]:
|
|
common_subdir = ''
|
|
break
|
|
|
|
unzip_to_dir = dest_dir
|
|
if common_subdir:
|
|
unzip_to_dir = os.path.join('/'.join(dest_dir.split('/')[:-1]), 'unzip_temp')
|
|
|
|
# Now do the actual decompress.
|
|
for member in zf.infolist():
|
|
# Path traversal defense copied from
|
|
# http://hg.python.org/cpython/file/tip/Lib/http/server.py#l789
|
|
words = member.filename.split('/')
|
|
path = unzip_to_dir
|
|
for word in words[:-1]:
|
|
drive, word = os.path.splitdrive(word)
|
|
head, word = os.path.split(word)
|
|
if word in (os.curdir, os.pardir, ''): continue
|
|
path = os.path.join(path, word)
|
|
zf.extract(member, unzip_to_dir)
|
|
|
|
if common_subdir:
|
|
try:
|
|
if os.path.exists(dest_dir):
|
|
remove_tree(dest_dir)
|
|
except:
|
|
pass
|
|
shutil.copytree(os.path.join(unzip_to_dir, common_subdir), dest_dir)
|
|
try:
|
|
remove_tree(unzip_to_dir)
|
|
except:
|
|
pass
|
|
except zipfile.BadZipfile as e:
|
|
print("Unzipping file '" + source_filename + "' failed due to reason: " + str(e) + "! Removing the corrupted zip file.")
|
|
rmfile(source_filename)
|
|
return False
|
|
except Exception as e:
|
|
print("Unzipping file '" + source_filename + "' failed due to reason: " + str(e))
|
|
return False
|
|
|
|
return True
|
|
|
|
# This function interprets whether the given string looks like a path to a directory instead of a file, without looking at the actual filesystem.
|
|
# 'a/b/c' points to directory, so does 'a/b/c/', but 'a/b/c.x' is parsed as a filename
|
|
def path_points_to_directory(path):
|
|
if path == '.':
|
|
return True
|
|
last_slash = max(path.rfind('/'), path.rfind('\\'))
|
|
last_dot = path.rfind('.')
|
|
no_suffix = last_dot < last_slash or last_dot == -1
|
|
if no_suffix:
|
|
return True
|
|
suffix = path[last_dot:]
|
|
if suffix == '.exe' or suffix == '.zip' or suffix == '.txt': # Very simple logic for the only file suffixes used by emsdk downloader. Other suffixes, like 'clang-3.2' are treated as dirs.
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
# On success, returns the filename on the disk pointing to the destination file that was produced
|
|
# On failure, returns None.
|
|
def download_file(url, dstpath, download_even_if_exists=False, filename_prefix = ''):
|
|
if VERBOSE: print('download_file(url=' + url + ', dstpath=' + dstpath + ')')
|
|
file_name = filename_prefix + url.split('/')[-1]
|
|
if path_points_to_directory(dstpath):
|
|
file_name = os.path.join(dstpath, file_name)
|
|
else:
|
|
file_name = dstpath
|
|
|
|
# Treat all relative destination paths as relative to the SDK root directory, not the current working directory.
|
|
file_name = sdk_path(file_name)
|
|
|
|
if os.path.exists(file_name) and not download_even_if_exists:
|
|
print("File '" + file_name + "' already downloaded, skipping.")
|
|
return file_name
|
|
try:
|
|
u = urlopen(url)
|
|
mkdir_p(os.path.dirname(file_name))
|
|
with open(file_name, 'wb') as f:
|
|
meta = u.info()
|
|
if hasattr(meta.getheaders, "Content-Length"):
|
|
file_size = int(meta.getheaders("Content-Length")[0])
|
|
print("Downloading: %s from %s, %s Bytes" % (file_name, url, file_size))
|
|
else:
|
|
file_size = 0
|
|
print("Downloading: %s from %s" % (file_name, url))
|
|
|
|
file_size_dl = 0
|
|
block_sz = 8192
|
|
while True:
|
|
buffer = u.read(block_sz)
|
|
if not buffer:
|
|
break
|
|
|
|
file_size_dl += len(buffer)
|
|
f.write(buffer)
|
|
if file_size:
|
|
status = r"%10d [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size)
|
|
status = status + chr(8)*(len(status)+1)
|
|
print(status, end=' ')
|
|
except HTTPError as e:
|
|
print("HTTP error with URL '" + url + "': " + str(e))
|
|
rmfile(file_name)
|
|
return None
|
|
# except Exception as e:
|
|
# print("Error downloading URL '" + url + "': " + str(e))
|
|
# rmfile(file_name)
|
|
# return None
|
|
return file_name
|
|
|
|
def download_text_file(url, dstpath, download_even_if_exists=False, filename_prefix=''):
|
|
filename = download_file(url, dstpath, download_even_if_exists, filename_prefix)
|
|
fix_lineendings(os.path.join(emsdk_path(), filename))
|
|
|
|
def run_get_output(cmd, cwd=None):
|
|
if VERBOSE: print('run_get_output(cmd=' + str(cmd) + ', cwd=' + str(cwd) + ')')
|
|
process = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, env=os.environ.copy(), universal_newlines=True)
|
|
(stdout, stderr) = process.communicate()
|
|
return (process.returncode, stdout, stderr)
|
|
|
|
# must_succeed: If false, the search is performed silently without printing out errors if not found. Empty string is returned if git is not found.
|
|
# If true, the search is required to succeed, and the execution will terminate with sys.exit(1) if not found.
|
|
def GIT(must_succeed=True):
|
|
# The order in the following is important, and specifies the preferred order of using the git tools.
|
|
# Primarily use git from emsdk if installed. If not, use system git.
|
|
gits = ['git/1.9.4/bin/git.exe', which('git')]
|
|
for git in gits:
|
|
try:
|
|
(ret, stdout, stderr) = run_get_output([git, '--version'])
|
|
if ret == 0:
|
|
return git
|
|
except:
|
|
pass
|
|
if must_succeed:
|
|
if WINDOWS:
|
|
print("ERROR: git executable was not found. Please install it by typing 'emsdk install git-1.9.4', or alternatively by installing it manually from http://git-scm.com/downloads . If you install git manually, remember to add it to PATH")
|
|
elif OSX:
|
|
print("ERROR: git executable was not found. Please install git for this operation! This can be done from http://git-scm.com/ , or by installing XCode and then the XCode Command Line Tools (see http://stackoverflow.com/questions/9329243/xcode-4-4-command-line-tools )")
|
|
elif LINUX:
|
|
print("ERROR: git executable was not found. Please install git for this operation! This can be probably be done using your package manager, see http://git-scm.com/book/en/Getting-Started-Installing-Git")
|
|
else:
|
|
print("ERROR: git executable was not found. Please install git for this operation!")
|
|
sys.exit(1)
|
|
return '' # Not found
|
|
|
|
def git_repo_version(repo_path):
|
|
(returncode, stdout, stderr) = run_get_output([GIT(), 'log', '-n', '1', '--pretty="%aD %H"'], cwd=repo_path)
|
|
if returncode == 0:
|
|
return stdout.strip()
|
|
else:
|
|
return ""
|
|
|
|
def git_clone(url, dstpath):
|
|
if VERBOSE: print('git_clone(url=' + url + ', dstpath=' + dstpath + ')')
|
|
if os.path.isdir(os.path.join(dstpath, '.git')):
|
|
print("Repository '" + url + "' already cloned to directory '" + dstpath + "', skipping.")
|
|
return True
|
|
mkdir_p(dstpath)
|
|
git_clone_args = []
|
|
if GIT_CLONE_SHALLOW:
|
|
git_clone_args += ['--depth', '1']
|
|
return run([GIT(), 'clone'] + git_clone_args + [url, dstpath]) == 0
|
|
|
|
def git_checkout_and_pull(repo_path, branch):
|
|
if VERBOSE: print('git_checkout_and_pull(repo_path=' + repo_path + ', branch=' + branch + ')')
|
|
ret = run([GIT(), 'fetch', 'origin'], repo_path)
|
|
if ret != 0: return False
|
|
try:
|
|
print("Fetching latest changes to the branch '" + branch + "' for '" + repo_path + "'...")
|
|
ret = run([GIT(), 'fetch', 'origin'], repo_path)
|
|
if ret != 0: return False
|
|
# run([GIT, 'checkout', '-b', branch, '--track', 'origin/'+branch], repo_path)
|
|
ret = run([GIT(), 'checkout', '--quiet', branch], repo_path) # this line assumes that the user has not gone and manually messed with the repo and added new remotes to ambiguate the checkout.
|
|
if ret != 0: return False
|
|
ret = run([GIT(), 'merge', '--ff-only', 'origin/'+branch], repo_path) # this line assumes that the user has not gone and made local changes to the repo
|
|
if ret != 0: return False
|
|
except:
|
|
print('git operation failed!')
|
|
return False
|
|
print("Successfully updated and checked out branch '" + branch + "' on repository '" + repo_path + "'")
|
|
print("Current repository version: " + git_repo_version(repo_path))
|
|
return True
|
|
|
|
def git_clone_checkout_and_pull(url, dstpath, branch):
|
|
if VERBOSE: print('git_clone_checkout_and_pull(url=' + url + ', dstpath=' + dstpath + ', branch=' + branch + ')')
|
|
success = git_clone(url, dstpath)
|
|
if not success:
|
|
return False
|
|
success = git_checkout_and_pull(dstpath, branch)
|
|
return success
|
|
|
|
# Each tool can have its own build type, or it can be overridden on the command line.
|
|
def decide_cmake_build_type(tool):
|
|
global CMAKE_BUILD_TYPE_OVERRIDE
|
|
if CMAKE_BUILD_TYPE_OVERRIDE:
|
|
return CMAKE_BUILD_TYPE_OVERRIDE
|
|
else:
|
|
return tool.cmake_build_type
|
|
|
|
# The root directory of the build.
|
|
def fastcomp_build_dir(tool):
|
|
generator_suffix = ''
|
|
if CMAKE_GENERATOR == 'Visual Studio 10': generator_suffix = '_vs2010'
|
|
elif CMAKE_GENERATOR == 'Visual Studio 11': generator_suffix = '_vs2012'
|
|
elif CMAKE_GENERATOR == 'Visual Studio 12': generator_suffix = '_vs2013'
|
|
elif CMAKE_GENERATOR == 'Visual Studio 14': generator_suffix = '_vs2015'
|
|
elif CMAKE_GENERATOR == 'MinGW Makefiles': generator_suffix = '_mingw'
|
|
|
|
bitness_suffix = '_32' if tool.bitness == 32 else '_64'
|
|
|
|
if hasattr(tool, 'git_branch'):
|
|
build_dir = 'build_' + tool.git_branch.replace(os.sep, '-') + generator_suffix + bitness_suffix
|
|
else:
|
|
build_dir = 'build_' + tool.version + generator_suffix + bitness_suffix
|
|
return build_dir
|
|
|
|
def exe_suffix(filename):
|
|
if WINDOWS and not filename.endswith('.exe'): return filename + '.exe'
|
|
else: return filename
|
|
|
|
# The directory where the binaries are produced. (relative to the installation root directory of the tool)
|
|
def fastcomp_build_bin_dir(tool):
|
|
build_dir = fastcomp_build_dir(tool)
|
|
if WINDOWS and 'Visual Studio' in CMAKE_GENERATOR:
|
|
old_llvm_bin_dir = os.path.join(build_dir, 'bin', decide_cmake_build_type(tool))
|
|
|
|
new_llvm_bin_dir = None
|
|
default_cmake_build_type = decide_cmake_build_type(tool)
|
|
cmake_build_types = [default_cmake_build_type, 'Release', 'RelWithDebInfo', 'MinSizeRel', 'Debug']
|
|
for build_type in cmake_build_types:
|
|
d = os.path.join(build_dir, build_type, 'bin')
|
|
if os.path.isfile(os.path.join(tool.installation_path(), d, exe_suffix('clang'))):
|
|
new_llvm_bin_dir = d
|
|
break
|
|
|
|
if new_llvm_bin_dir and os.path.exists(os.path.join(tool.installation_path(), new_llvm_bin_dir)): return new_llvm_bin_dir
|
|
elif os.path.exists(os.path.join(tool.installation_path(), old_llvm_bin_dir)): return old_llvm_bin_dir
|
|
return os.path.join(build_dir, default_cmake_build_type, 'bin')
|
|
else:
|
|
return os.path.join(build_dir, 'bin')
|
|
|
|
def build_env(generator):
|
|
build_env = os.environ.copy()
|
|
|
|
# To work around a build issue with older Mac OS X builds, add -stdlib=libc++ to all builds.
|
|
# See https://groups.google.com/forum/#!topic/emscripten-discuss/5Or6QIzkqf0
|
|
if OSX: build_env['CXXFLAGS'] = ((build_env['CXXFLAGS'] + ' ') if hasattr(build_env, 'CXXFLAGS') else '') + '-stdlib=libc++'
|
|
|
|
if 'Visual Studio 14' in generator or 'Visual Studio 2015' in generator:
|
|
build_env['VCTargetsPath'] = os.path.join(os.environ['ProgramFiles(x86)'], 'MSBuild/Microsoft.Cpp/v4.0/V140')
|
|
|
|
# CMake and VS2015 cl.exe needs to have mspdb140.dll et al. in its PATH.
|
|
vc_bin_paths = [os.path.join(os.environ['ProgramFiles'], 'Microsoft Visual Studio 14.0\\VC\\bin'),
|
|
os.path.join(os.environ['ProgramFiles(x86)'], 'Microsoft Visual Studio 14.0\\VC\\bin')]
|
|
for path in vc_bin_paths:
|
|
if os.path.isdir(path):
|
|
build_env['PATH'] = build_env['PATH'] + ';' + path
|
|
|
|
elif 'Visual Studio 12' in generator or 'Visual Studio 2013' in generator:
|
|
build_env['VCTargetsPath'] = os.path.join(os.environ['ProgramFiles(x86)'], 'MSBuild/Microsoft.Cpp/v4.0/V120')
|
|
|
|
return build_env
|
|
|
|
def get_generator_for_sln_file(sln_file):
|
|
contents = open(sln_file, 'r').read()
|
|
if '# Visual Studio Express 2015' in contents or '# Visual Studio 2015' in contents or '# Visual Studio 14' in contents:
|
|
return 'Visual Studio 14'
|
|
if '# Visual Studio Express 2013' in contents or '# Visual Studio 2013' in contents or '# Visual Studio 12' in contents:
|
|
return 'Visual Studio 12'
|
|
raise Exception('Unknown generator used to build solution file ' + sln_file)
|
|
|
|
def find_msbuild(sln_file):
|
|
search_paths_vs2015 = [os.path.join(os.environ['ProgramFiles'], 'MSBuild/14.0/Bin/amd64'),
|
|
os.path.join(os.environ['ProgramFiles(x86)'], 'MSBuild/14.0/Bin/amd64'),
|
|
os.path.join(os.environ['ProgramFiles'], 'MSBuild/14.0/Bin'),
|
|
os.path.join(os.environ['ProgramFiles(x86)'], 'MSBuild/14.0/Bin')]
|
|
search_paths_vs2013 = [os.path.join(os.environ['ProgramFiles'], 'MSBuild/12.0/Bin/amd64'),
|
|
os.path.join(os.environ['ProgramFiles(x86)'], 'MSBuild/12.0/Bin/amd64'),
|
|
os.path.join(os.environ['ProgramFiles'], 'MSBuild/12.0/Bin'),
|
|
os.path.join(os.environ['ProgramFiles(x86)'], 'MSBuild/12.0/Bin')]
|
|
search_paths_old = [os.path.join(os.environ["WINDIR"], 'Microsoft.NET/Framework/v4.0.30319')]
|
|
generator = get_generator_for_sln_file(sln_file)
|
|
if generator == 'Visual Studio 14':
|
|
search_paths = search_paths_vs2015
|
|
elif generator == 'Visual Studio 12':
|
|
search_paths = search_paths_vs2013 + search_paths_old
|
|
else:
|
|
raise Exception('Unknown generator!')
|
|
|
|
for path in search_paths:
|
|
p = os.path.join(path, 'MSBuild.exe')
|
|
if os.path.isfile(p): return p
|
|
|
|
def make_build(build_root, build_type, build_target_platform='x64'):
|
|
if VERBOSE: print('make_build(build_root=' + build_root + ', build_type=' + build_type + ', build_target_platform=' + build_target_platform + ')')
|
|
global CPU_CORES
|
|
if CPU_CORES > 1:
|
|
print('Performing a parallel build with ' + str(CPU_CORES) + ' cores.')
|
|
else:
|
|
print('Performing a singlethreaded build.')
|
|
|
|
generator_to_use = CMAKE_GENERATOR
|
|
|
|
if WINDOWS:
|
|
if 'Visual Studio' in CMAKE_GENERATOR:
|
|
solution_name = subprocess.check_output(['dir', '/b', '*.sln'], shell=True, cwd=build_root).strip()
|
|
generator_to_use = get_generator_for_sln_file(os.path.join(build_root, solution_name))
|
|
# Disabled for now: Don't pass /maxcpucount argument to msbuild, since it looks like when building, msbuild already automatically spawns the full amount of logical
|
|
# cores the system has, and passing the number of logical cores here has been observed to give a quadratic N*N explosion on the number of spawned processes
|
|
# (e.g. on a Core i7 5960X with 16 logical cores, it would spawn 16*16=256 cl.exe processes, which would start crashing when running out of system memory)
|
|
# make = [find_msbuild(os.path.join(build_root, solution_name)), '/maxcpucount:' + str(CPU_CORES), '/t:Build', '/p:Configuration='+build_type, '/nologo', '/verbosity:minimal', solution_name]
|
|
make = [find_msbuild(os.path.join(build_root, solution_name)), '/t:Build', '/p:Configuration='+build_type, '/p:Platform='+build_target_platform, '/nologo', '/verbosity:minimal', solution_name]
|
|
else:
|
|
make = ['mingw32-make', '-j' + str(CPU_CORES)]
|
|
else:
|
|
make = ['make', '-j' + str(CPU_CORES)]
|
|
|
|
# Build
|
|
try:
|
|
print('Running build: ' + str(make))
|
|
ret = subprocess.check_call(make, cwd=build_root, env=build_env(generator_to_use))
|
|
if ret != 0:
|
|
print('Build failed with exit code ' + ret + '!', file=sys.stderr)
|
|
print('Working directory: ' + build_root, file=sys.stderr)
|
|
return False
|
|
except Exception as e:
|
|
print('Build failed due to exception!', file=sys.stderr)
|
|
print('Working directory: ' + build_root, file=sys.stderr)
|
|
print(str(e), file=sys.stderr)
|
|
return False
|
|
|
|
return True
|
|
|
|
def cmake_configure(generator, build_root, src_root, build_type, extra_cmake_args = []):
|
|
if VERBOSE: print('cmake_configure(generator=' + str(generator) + ', build_root=' + str(build_root) + ', src_root=' + str(src_root) + ', build_type=' + str(build_type) + ', extra_cmake_args=' + str(extra_cmake_args) + ')')
|
|
# Configure
|
|
if not os.path.isdir(build_root): os.mkdir(build_root) # Create build output directory if it doesn't yet exist.
|
|
try:
|
|
if generator: generator = ['-G', generator]
|
|
else: generator = []
|
|
cmdline = ['cmake'] + generator + ['-DCMAKE_BUILD_TYPE='+build_type, '-DPYTHON_EXECUTABLE='+sys.executable] + extra_cmake_args + [src_root]
|
|
print('Running CMake: ' + str(cmdline))
|
|
def quote_parens(x):
|
|
if ' ' in x:
|
|
return '"' + x.replace('"', '\\"') + '"'
|
|
else:
|
|
return x
|
|
open(os.path.join(build_root, 'recmake.' + ('bat' if WINDOWS else 'sh')), 'w').write(' '.join(map(quote_parens, cmdline))) # Create a file 'recmake.bat/sh' in the build root that user can call to manually recmake the build tree with the previous build params
|
|
ret = subprocess.check_call(cmdline, cwd=build_root, env=build_env(CMAKE_GENERATOR))
|
|
if ret != 0:
|
|
print('CMake invocation failed with exit code ' + ret + '!', file=sys.stderr)
|
|
print('Working directory: ' + build_root, file=sys.stderr)
|
|
return False
|
|
except OSError as e:
|
|
if e.errno == errno.ENOENT:
|
|
print(str(e), file=sys.stderr)
|
|
print('Could not run CMake, perhaps it has not been installed?', file=sys.stderr)
|
|
if WINDOWS:
|
|
print('Installing this package requires CMake. Get it from http://www.cmake.org/', file=sys.stderr)
|
|
elif LINUX:
|
|
print('Installing this package requires CMake. Get it via your system package manager (e.g. sudo apt-get install cmake), or from http://www.cmake.org/', file=sys.stderr)
|
|
elif OSX:
|
|
print('Installing this package requires CMake. Get it via a OSX package manager (Homebrew: "brew install cmake", or MacPorts: "sudo port install cmake"), or from http://www.cmake.org/', file=sys.stderr)
|
|
return False
|
|
raise
|
|
except Exception as e:
|
|
print('CMake invocation failed due to exception!', file=sys.stderr)
|
|
print('Working directory: ' + build_root, file=sys.stderr)
|
|
print(str(e), file=sys.stderr)
|
|
return False
|
|
|
|
return True
|
|
|
|
def build_fastcomp_tool(tool):
|
|
if VERBOSE: print('build_fastcomp_tool(' + str(tool) + ')')
|
|
fastcomp_root = tool.installation_path()
|
|
fastcomp_src_root = os.path.join(fastcomp_root, 'src')
|
|
if hasattr(tool, 'git_branch'): # Does this tool want to be git cloned from github?
|
|
success = git_clone_checkout_and_pull(tool.download_url(), fastcomp_src_root, tool.git_branch)
|
|
if not success: return False
|
|
clang_root = os.path.join(fastcomp_src_root, 'tools/clang')
|
|
success = git_clone_checkout_and_pull(tool.clang_url, clang_root, tool.git_branch)
|
|
if not success: return False
|
|
else: # Not a git cloned tool, so instead download from git tagged releases
|
|
success = download_and_unzip(tool.download_url(), fastcomp_src_root, filename_prefix='llvm-e')
|
|
if not success: return False
|
|
success = download_and_unzip(tool.windows_clang_url if WINDOWS else tool.unix_clang_url, os.path.join(fastcomp_src_root, 'tools/clang'), filename_prefix='clang-e')
|
|
if not success: return False
|
|
|
|
cmake_generator = CMAKE_GENERATOR
|
|
if 'Visual Studio' in CMAKE_GENERATOR and tool.bitness == 64:
|
|
cmake_generator += ' Win64'
|
|
|
|
build_dir = fastcomp_build_dir(tool)
|
|
build_root = os.path.join(fastcomp_root, build_dir)
|
|
|
|
build_type = decide_cmake_build_type(tool)
|
|
|
|
# Configure
|
|
global BUILD_FOR_TESTING, ENABLE_LLVM_ASSERTIONS
|
|
tests_arg = 'ON' if BUILD_FOR_TESTING else 'OFF'
|
|
|
|
enable_assertions = ENABLE_LLVM_ASSERTIONS.lower() == 'on' or (ENABLE_LLVM_ASSERTIONS == 'auto' and build_type.lower() != 'release' and build_type.lower() != 'minsizerel')
|
|
|
|
args = ['-DLLVM_TARGETS_TO_BUILD=X86;JSBackend', '-DLLVM_INCLUDE_EXAMPLES=OFF', '-DCLANG_INCLUDE_EXAMPLES=OFF', '-DLLVM_INCLUDE_TESTS=' + tests_arg, '-DCLANG_INCLUDE_TESTS=' + tests_arg, '-DLLVM_ENABLE_ASSERTIONS=' + ('ON' if enable_assertions else 'OFF')]
|
|
success = cmake_configure(cmake_generator, build_root, fastcomp_src_root, build_type, args)
|
|
if not success: return False
|
|
|
|
# Make
|
|
success = make_build(build_root, build_type, 'x64' if tool.bitness == 64 else 'Win32')
|
|
return success
|
|
|
|
# Emscripten asm.js optimizer build scripts:
|
|
def optimizer_build_root(tool):
|
|
build_root = tool.installation_path().strip()
|
|
if build_root.endswith('/') or build_root.endswith('\\'): build_root = build_root[:-1]
|
|
generator_prefix = cmake_generator_prefix()
|
|
build_root = build_root + generator_prefix + '_' + str(tool.bitness) + 'bit_optimizer'
|
|
return build_root
|
|
|
|
def uninstall_optimizer(tool):
|
|
if VERBOSE: print('uninstall_optimizer(' + str(tool) + ')')
|
|
build_root = optimizer_build_root(tool)
|
|
print("Deleting path '" + build_root + "'")
|
|
try:
|
|
remove_tree(build_root)
|
|
os.remove(build_root)
|
|
except:
|
|
pass
|
|
|
|
def is_optimizer_installed(tool):
|
|
build_root = optimizer_build_root(tool)
|
|
return os.path.exists(build_root)
|
|
|
|
def build_optimizer_tool(tool):
|
|
if VERBOSE: print('build_optimizer_tool(' + str(tool) + ')')
|
|
src_root = os.path.join(tool.installation_path(), 'tools', 'optimizer')
|
|
build_root = optimizer_build_root(tool)
|
|
build_type = decide_cmake_build_type(tool)
|
|
|
|
# Configure
|
|
cmake_generator = CMAKE_GENERATOR
|
|
if 'Visual Studio' in CMAKE_GENERATOR and tool.bitness == 64:
|
|
cmake_generator += ' Win64'
|
|
success = cmake_configure(cmake_generator, build_root, src_root, build_type)
|
|
if not success: return False
|
|
|
|
# Make
|
|
success = make_build(build_root, build_type, 'x64' if tool.bitness == 64 else 'Win32')
|
|
return success
|
|
|
|
|
|
# Binaryen build scripts:
|
|
def binaryen_build_root(tool):
|
|
build_root = tool.installation_path().strip()
|
|
if build_root.endswith('/') or build_root.endswith('\\'): build_root = build_root[:-1]
|
|
generator_prefix = cmake_generator_prefix()
|
|
build_root = build_root + generator_prefix + '_' + str(tool.bitness) + 'bit_binaryen'
|
|
return build_root
|
|
|
|
def uninstall_binaryen(tool):
|
|
if VERBOSE: print('uninstall_binaryen(' + str(tool) + ')')
|
|
build_root = binaryen_build_root(tool)
|
|
print("Deleting path '" + build_root + "'")
|
|
try:
|
|
remove_tree(build_root)
|
|
os.remove(build_root)
|
|
except:
|
|
pass
|
|
|
|
def is_binaryen_installed(tool):
|
|
build_root = binaryen_build_root(tool)
|
|
return os.path.exists(build_root)
|
|
|
|
def build_binaryen_tool(tool):
|
|
if VERBOSE: print('build_binaryen_tool(' + str(tool) + ')')
|
|
src_root = tool.installation_path()
|
|
build_root = binaryen_build_root(tool)
|
|
build_type = decide_cmake_build_type(tool)
|
|
|
|
# Configure
|
|
args = []
|
|
|
|
cmake_generator = CMAKE_GENERATOR
|
|
if 'Visual Studio' in CMAKE_GENERATOR:
|
|
if tool.bitness == 64: cmake_generator += ' Win64'
|
|
if BUILD_FOR_TESTING: args += ['-DRUN_STATIC_ANALYZER=1']
|
|
|
|
success = cmake_configure(cmake_generator, build_root, src_root, build_type, args)
|
|
if not success: return False
|
|
|
|
# Make
|
|
success = make_build(build_root, build_type, 'x64' if tool.bitness == 64 else 'Win32')
|
|
|
|
# Deploy scripts needed from source repository to build directory
|
|
remove_tree(os.path.join(build_root, 'scripts'))
|
|
shutil.copytree(os.path.join(src_root, 'scripts'), os.path.join(build_root, 'scripts'))
|
|
remove_tree(os.path.join(build_root, 'src', 'js'))
|
|
shutil.copytree(os.path.join(src_root, 'src', 'js'), os.path.join(build_root, 'src', 'js'))
|
|
shutil.copyfile(os.path.join(src_root, 'bin', 'binaryen.js'), os.path.join(build_root, 'bin', 'binaryen.js'))
|
|
shutil.copyfile(os.path.join(src_root, 'bin', 'wasm.js'), os.path.join(build_root, 'bin', 'wasm.js'))
|
|
|
|
return success
|
|
|
|
|
|
def download_and_unzip(zipfile, dest_dir, download_even_if_exists=False, filename_prefix = ''):
|
|
if VERBOSE: print('download_and_unzip(zipfile=' + zipfile + ', dest_dir=' + dest_dir + ')')
|
|
if not download_even_if_exists and num_files_in_directory(dest_dir) > 0:
|
|
print("The contents of file '" + zipfile + "' already exist in destination '" + dest_dir + "', skipping.")
|
|
return True
|
|
dst_file = download_file(urljoin(emsdk_packages_url, zipfile), 'zips/', download_even_if_exists, filename_prefix)
|
|
if not dst_file:
|
|
return False
|
|
if zipfile.endswith('.zip'):
|
|
return unzip(dst_file, dest_dir, unpack_even_if_exists=download_even_if_exists)
|
|
else:
|
|
return untargz(dst_file, dest_dir, unpack_even_if_exists=download_even_if_exists)
|
|
|
|
def to_native_path(p):
|
|
if WINDOWS:
|
|
return to_unix_path(p).replace('/', '\\')
|
|
else:
|
|
return to_unix_path(p)
|
|
|
|
# Finds and returns a list of the directories that need to be added to PATH for the given set of tools.
|
|
def get_required_path(active_tools):
|
|
path_add = [to_native_path(emsdk_path())]
|
|
for tool in active_tools:
|
|
if hasattr(tool, 'activated_path'):
|
|
path_add += [to_native_path(tool.expand_vars(tool.activated_path))]
|
|
return path_add
|
|
|
|
# Returns the absolute path to the file '.emscripten' for the current user on this system.
|
|
def dot_emscripten_path():
|
|
return os.path.join(emscripten_config_directory, ".emscripten")
|
|
|
|
dot_emscripten = {}
|
|
|
|
def parse_key_value(line):
|
|
if not line:
|
|
return ('', '')
|
|
eq = line.find('=')
|
|
if eq != -1:
|
|
key = line[0:eq].strip()
|
|
value = line[eq+1:].strip()
|
|
return (key, value)
|
|
else:
|
|
return (key, '')
|
|
|
|
def load_dot_emscripten():
|
|
global dot_emscripten
|
|
dot_emscripten = {}
|
|
lines = []
|
|
try:
|
|
lines = open(dot_emscripten_path(), "r").read().split('\n')
|
|
except:
|
|
pass
|
|
for line in lines:
|
|
try:
|
|
(key, value) = parse_key_value(line)
|
|
if value != '':
|
|
dot_emscripten[key] = value
|
|
# print("Got '" + key + "' = '" + value + "'")
|
|
except:
|
|
pass
|
|
|
|
def generate_dot_emscripten(active_tools):
|
|
global emscripten_config_directory
|
|
if emscripten_config_directory == emsdk_path():
|
|
temp_dir = sdk_path('tmp')
|
|
mkdir_p(temp_dir)
|
|
embedded = True
|
|
else:
|
|
temp_dir = tempfile.gettempdir().replace('\\', '/')
|
|
embedded = False
|
|
|
|
has_spidermonkey = False
|
|
has_node = False
|
|
|
|
cfg = 'import os\n'
|
|
|
|
if embedded:
|
|
cfg += "emsdk_path=os.path.dirname(os.environ.get('EM_CONFIG')).replace('\\\\', '/')\n"
|
|
|
|
for tool in active_tools:
|
|
tool_cfg = tool.activated_config()
|
|
if tool_cfg:
|
|
tool_cfg = tool_cfg.replace(';', '\n')
|
|
if 'SPIDERMONKEY_ENGINE=' in tool_cfg: has_spidermonkey = True
|
|
if 'NODE_JS=' in tool_cfg: has_node = True
|
|
cfg += tool_cfg + '\n'
|
|
|
|
# These two vars must always be defined, even though they might not exist.
|
|
if not has_spidermonkey: cfg += "SPIDERMONKEY_ENGINE = ''\n"
|
|
if not has_node:
|
|
node_fallback = which('nodejs')
|
|
if not node_fallback:
|
|
node_fallback = 'node'
|
|
cfg += "NODE_JS = '" + node_fallback + "'\n"
|
|
|
|
cfg += '''V8_ENGINE = ''
|
|
TEMP_DIR = ''' + "'" + temp_dir + "'" + '''
|
|
COMPILER_ENGINE = NODE_JS
|
|
JS_ENGINES = [NODE_JS]
|
|
'''
|
|
|
|
if embedded:
|
|
cfg = cfg.replace(emscripten_config_directory, "' + emsdk_path + '")
|
|
|
|
with open(dot_emscripten_path(), "w") as text_file: text_file.write(cfg)
|
|
|
|
# Clear old cached emscripten content.
|
|
try:
|
|
remove_tree(os.path.join(emscripten_config_directory, ".emscripten_cache"))
|
|
os.remove(os.path.join(emscripten_config_directory, ".emscripten_sanity"))
|
|
os.remove(os.path.join(emscripten_config_directory, ".emscripten_cache__last_clear"))
|
|
except:
|
|
pass
|
|
|
|
print("The Emscripten configuration file " + os.path.normpath(dot_emscripten_path()) + " has been rewritten with the following contents:")
|
|
print('')
|
|
print(cfg.strip())
|
|
print('')
|
|
|
|
path_add = get_required_path(active_tools)
|
|
if not WINDOWS:
|
|
emsdk_env = os.path.relpath(sdk_path('emsdk_env.sh'))
|
|
if not '/' in emsdk_env:
|
|
emsdk_env = './emsdk_env.sh'
|
|
print("To conveniently access the selected set of tools from the command line, consider adding the following directories to PATH, or call 'source " + emsdk_env + "' to do this for you.")
|
|
print('')
|
|
print(' ' + ENVPATH_SEPARATOR.join(path_add))
|
|
|
|
def find_msbuild_dir():
|
|
if 'ProgramFiles' in os.environ and os.environ['ProgramFiles']:
|
|
program_files = os.environ['ProgramFiles']
|
|
else:
|
|
program_files = 'C:/Program Files'
|
|
if 'ProgramFiles(x86)' in os.environ and os.environ['ProgramFiles(x86)']:
|
|
program_files_x86 = os.environ['ProgramFiles(x86)']
|
|
else:
|
|
program_files_x86 = 'C:/Program Files (x86)'
|
|
MSBUILDX86_DIR = os.path.join(program_files_x86, "MSBuild/Microsoft.Cpp/v4.0/Platforms")
|
|
MSBUILD_DIR = os.path.join(program_files, "MSBuild/Microsoft.Cpp/v4.0/Platforms")
|
|
if os.path.exists(MSBUILDX86_DIR):
|
|
return MSBUILDX86_DIR
|
|
elif os.path.exists(MSBUILD_DIR):
|
|
return MSBUILD_DIR
|
|
else:
|
|
return '' # No MSbuild installed.
|
|
|
|
def get_installed_vstool_version(installed_path):
|
|
try:
|
|
return open(installed_path + "/version.txt", "r").read()
|
|
except:
|
|
return None
|
|
|
|
class Tool:
|
|
def __init__(self, data):
|
|
# Convert the dictionary representation of the tool in 'data' to members of this class for convenience.
|
|
for key in data:
|
|
if sys.version_info < (3,) and isinstance(data[key], unicode):
|
|
setattr(self, key, data[key].encode('Latin-1'))
|
|
else:
|
|
setattr(self, key, data[key])
|
|
|
|
def __str__(self):
|
|
s = self.id + '-' + self.version
|
|
if hasattr(self, 'bitness'):
|
|
s += '-' + str(self.bitness) + 'bit'
|
|
return s
|
|
|
|
def expand_vars(self, str):
|
|
if WINDOWS and '%MSBuildPlatformsDir%' in str:
|
|
str = str.replace('%MSBuildPlatformsDir%', find_msbuild_dir())
|
|
if '%cmake_build_type_on_win%' in str:
|
|
str = str.replace('%cmake_build_type_on_win%', (decide_cmake_build_type(self) + '/') if WINDOWS else '')
|
|
if '%installation_dir%' in str:
|
|
str = str.replace('%installation_dir%', sdk_path(self.installation_dir()))
|
|
if '%generator_prefix%' in str:
|
|
str = str.replace('%generator_prefix%', cmake_generator_prefix())
|
|
str = str.replace('%.exe%', '.exe' if WINDOWS else '')
|
|
if '%fastcomp_build_dir%' in str:
|
|
str = str.replace('%fastcomp_build_dir%', fastcomp_build_dir(self))
|
|
if '%fastcomp_build_bin_dir%' in str:
|
|
str = str.replace('%fastcomp_build_bin_dir%', fastcomp_build_bin_dir(self))
|
|
return str
|
|
|
|
# Return true if this tool requires building from source, and false if this is a precompiled tool.
|
|
def needs_compilation(self):
|
|
if hasattr(self, 'cmake_build_type'): return True
|
|
|
|
if hasattr(self, 'uses'):
|
|
for tool_name in self.uses:
|
|
tool = find_tool(tool_name)
|
|
if not tool:
|
|
if VERBOSE: print('Tool ' + str(self) + ' depends on ' + tool_name + ' which does not exist!')
|
|
continue
|
|
if tool.needs_compilation(): return True
|
|
|
|
return False
|
|
|
|
# Specifies the target path where this tool will be installed to. This could either be a directory or a filename (e.g. in case of node.js)
|
|
def installation_path(self):
|
|
if WINDOWS and hasattr(self, 'windows_install_path'):
|
|
pth = self.expand_vars(self.windows_install_path)
|
|
return sdk_path(pth)
|
|
if hasattr(self, 'install_path'):
|
|
pth = self.expand_vars(self.install_path)
|
|
return sdk_path(pth)
|
|
p = self.version
|
|
if hasattr(self, 'bitness') and (not hasattr(self, 'append_bitness') or self.append_bitness):
|
|
p += '_' + str(self.bitness) + 'bit'
|
|
return sdk_path(os.path.join(self.id, p))
|
|
|
|
# Specifies the target directory this tool will be installed to.
|
|
def installation_dir(self):
|
|
dir = self.installation_path()
|
|
if path_points_to_directory(dir):
|
|
return dir
|
|
else:
|
|
return os.path.dirname(dir)
|
|
|
|
# Returns the configuration item that needs to be added to .emscripten to make this Tool active for the current user.
|
|
def activated_config(self):
|
|
if hasattr(self, 'activated_cfg'):
|
|
return to_unix_path(self.expand_vars(self.activated_cfg))
|
|
else:
|
|
return ''
|
|
|
|
def activated_environment(self):
|
|
if hasattr(self, 'activated_env'):
|
|
return self.expand_vars(self.activated_env)
|
|
else:
|
|
return ''
|
|
|
|
def compatible_with_this_os(self):
|
|
if hasattr(self, 'os'):
|
|
if self.os == 'all':
|
|
return True
|
|
if (WINDOWS and 'win' in self.os) or (LINUX and ('linux' in self.os or 'unix' in self.os)) or (OSX and ('osx' in self.os or 'unix' in self.os)):
|
|
return True
|
|
else:
|
|
return False
|
|
else:
|
|
if not hasattr(self, 'osx_url') and not hasattr(self, 'windows_url') and not hasattr(self, 'unix_url') and not hasattr(self, 'linux_url'):
|
|
return True
|
|
|
|
if OSX and hasattr(self, 'osx_url'):
|
|
return True
|
|
|
|
if LINUX and hasattr(self, 'linux_url'):
|
|
return True
|
|
|
|
if WINDOWS and (hasattr(self, 'windows_url') or hasattr(self, 'windows_install_path')):
|
|
return True
|
|
|
|
if (LINUX or OSX) and hasattr(self, 'unix_url'):
|
|
return True
|
|
|
|
return hasattr(self, 'url')
|
|
|
|
def is_installed(self):
|
|
# If this tool/sdk depends on other tools, require that all dependencies are installed for this tool to count as being installed.
|
|
if hasattr(self, 'uses'):
|
|
for tool_name in self.uses:
|
|
tool = find_tool(tool_name)
|
|
if tool == None:
|
|
print("Manifest error: No tool by name '" + tool_name + "' found! This may indicate an internal SDK error!")
|
|
return False
|
|
if not tool.is_installed():
|
|
return False
|
|
|
|
if self.download_url() != None:
|
|
# For e.g. fastcomp clang from git repo, the activated PATH is the directory where the compiler is built to, and installation_path is
|
|
# the directory where the source tree exists. To distinguish between multiple packages sharing the same source
|
|
# (clang-master-32bit, clang-master-64bit, clang-incoming-32bit and clang-incoming-64bit each share the same git repo), require
|
|
# that in addition to the installation directory, each item in the activated PATH must exist.
|
|
activated_path = self.expand_vars(self.activated_path).split(';') if hasattr(self, 'activated_path') else [self.installation_path()]
|
|
def each_path_exists(pathlist):
|
|
for path in pathlist:
|
|
if not os.path.exists(path): return False
|
|
return True
|
|
content_exists = os.path.exists(self.installation_path()) and each_path_exists(activated_path) and (os.path.isfile(self.installation_path()) or num_files_in_directory(self.installation_path()) > 0)
|
|
|
|
if self.id == 'vs-tool': # vs-tool is a special tool since all versions must be installed to the same dir, so dir name will not differentiate the version.
|
|
return content_exists and get_installed_vstool_version(self.installation_path()) == self.version
|
|
elif hasattr(self, 'custom_is_installed_script'):
|
|
if self.custom_is_installed_script == 'is_optimizer_installed': return is_optimizer_installed(self)
|
|
elif self.custom_is_installed_script == 'is_binaryen_installed': return is_binaryen_installed(self)
|
|
else: raise Exception('Unknown custom_is_installed_script directive "' + self.custom_is_installed_script + '"!')
|
|
else:
|
|
return content_exists
|
|
else:
|
|
return True # This tool does not contain downloadable elements, so it is installed by default.
|
|
|
|
def is_active(self):
|
|
if not self.is_installed():
|
|
return False
|
|
|
|
if self.id == 'vs-tool':
|
|
return True # vs-tool is a special tool since all versions must be installed to the same dir, which means that if this tool is installed, it is also active.
|
|
|
|
# All dependencies of this tool must be active as well.
|
|
deps = self.dependencies()
|
|
for tool in deps:
|
|
if not tool.is_active():
|
|
return False
|
|
|
|
activated_cfg = self.activated_config()
|
|
if activated_cfg == '':
|
|
return len(deps) > 0
|
|
|
|
activated_cfg = activated_cfg.split(';')
|
|
for cfg in activated_cfg:
|
|
cfg = cfg.strip()
|
|
(key, value) = parse_key_value(cfg)
|
|
if not key in dot_emscripten: return False
|
|
|
|
# If running in embedded mode, all paths are stored dynamically relative to the emsdk root, so normalize those first.
|
|
dot_emscripten_key = dot_emscripten[key].replace("' + emsdk_path + '", emsdk_path())
|
|
if dot_emscripten_key != value:
|
|
return False
|
|
return True
|
|
|
|
# Returns true if the system environment variables requires by this tool are currently active.
|
|
def is_env_active(self):
|
|
if hasattr(self, 'activated_env'):
|
|
(key, value) = parse_key_value(self.activated_environment())
|
|
if not key in os.environ or to_unix_path(os.environ[key]) != to_unix_path(value):
|
|
return False
|
|
# if WINDOWS:
|
|
# env_var = win_get_active_environment_variable(key)
|
|
# if env_var != value:
|
|
# return False
|
|
if hasattr(self, 'activated_path'):
|
|
path = self.expand_vars(self.activated_path).replace('\\', '/')
|
|
path = path.split(ENVPATH_SEPARATOR)
|
|
for p in path:
|
|
path_items = os.environ['PATH'].replace('\\', '/').split(ENVPATH_SEPARATOR)
|
|
if not normalized_contains(path_items, p):
|
|
return False
|
|
return True
|
|
|
|
def win_activate_env_vars(self, permanently_activate):
|
|
if WINDOWS and hasattr(self, 'activated_env'):
|
|
(key, value) = parse_key_value(self.activated_environment())
|
|
if permanently_activate:
|
|
win_delete_environment_variable(key, False) # If there is an env var for the LOCAL USER with same name, it will hide the system var, so must remove that first.
|
|
|
|
win_set_environment_variable(key, value, permanently_activate)
|
|
|
|
# If this tool can be installed on this system, this function returns True.
|
|
# Otherwise, this function returns a string that describes the reason why this tool is not available.
|
|
def can_be_installed(self):
|
|
if self.id == 'vs-tool':
|
|
msbuild_dir = find_msbuild_dir()
|
|
if len(msbuild_dir) > 0:
|
|
return True
|
|
else:
|
|
return "Visual Studio 2010 was not found!"
|
|
else:
|
|
return True
|
|
|
|
def download_url(self):
|
|
if WINDOWS and hasattr(self, 'windows_url'):
|
|
return self.windows_url
|
|
elif OSX and hasattr(self, 'osx_url'):
|
|
return self.osx_url
|
|
elif LINUX and hasattr(self, 'linux_url'):
|
|
return self.linux_url
|
|
elif (OSX or LINUX) and hasattr(self, 'unix_url'):
|
|
return self.unix_url
|
|
elif hasattr(self, 'url'):
|
|
return self.url
|
|
else:
|
|
return None
|
|
|
|
def install(self):
|
|
if self.can_be_installed() != True:
|
|
print("The tool '" + str(self) + "' is not available due to the reason: " + self.can_be_installed())
|
|
return False
|
|
|
|
if self.id == 'sdk':
|
|
print("Installing SDK '" + str(self) + "'..")
|
|
for tool_name in self.uses:
|
|
tool = find_tool(tool_name)
|
|
if tool == None:
|
|
print("Manifest error: No tool by name '" + tool_name + "' found! This may indicate an internal SDK error!")
|
|
success = tool.install()
|
|
if not success:
|
|
return False
|
|
print("Done installing SDK '" + str(self) + "'.")
|
|
return True
|
|
else:
|
|
print("Installing tool '" + str(self) + "'..")
|
|
url = self.download_url()
|
|
|
|
if hasattr(self, 'custom_install_script') and self.custom_install_script == 'build_fastcomp':
|
|
success = build_fastcomp_tool(self)
|
|
elif hasattr(self, 'git_branch'):
|
|
success = git_clone_checkout_and_pull(url, self.installation_path(), self.git_branch)
|
|
elif url.endswith('zip') or url.endswith('.tar') or url.endswith('.gz'):
|
|
download_even_if_exists = (self.id == 'vs-tool')
|
|
success = download_and_unzip(url, self.installation_path(), download_even_if_exists, filename_prefix=self.zipfile_prefix if hasattr(self, 'zipfile_prefix') else '')
|
|
else:
|
|
dst_file = download_file(urljoin(emsdk_packages_url, self.download_url()), self.installation_path())
|
|
if dst_file: success = True
|
|
else: success = False
|
|
|
|
if success and hasattr(self, 'custom_install_script'):
|
|
if self.custom_install_script == 'build_optimizer': success = build_optimizer_tool(self)
|
|
elif self.custom_install_script == 'build_fastcomp': pass # 'build_fastcomp' is a special one that does the download on its own, others do the download manually.
|
|
elif self.custom_install_script == 'build_binaryen': success = build_binaryen_tool(self)
|
|
else: raise Exception('Unknown custom_install_script command "' + self.custom_install_script + '"!')
|
|
|
|
if not success:
|
|
print("Installation failed!")
|
|
return False
|
|
print("Done installing tool '" + str(self) + "'.")
|
|
|
|
# Sanity check that the installation succeeded, and if so, remove unneeded leftover installation files.
|
|
if self.is_installed():
|
|
self.cleanup_temp_install_files()
|
|
else:
|
|
print("Warning: The installation of '" + str(self) + "' seems to have failed, but no error was detected. Either something went wrong with the installation, or this may indicate an internal emsdk error.")
|
|
return True
|
|
|
|
def cleanup_temp_install_files(self):
|
|
url = self.download_url()
|
|
if url.endswith('zip') or url.endswith('.tar') or url.endswith('.gz'):
|
|
file_name = url.split('/')[-1]
|
|
tempzipfile = os.path.join('zips/', file_name)
|
|
rmfile(tempzipfile)
|
|
|
|
def uninstall(self):
|
|
if not self.is_installed():
|
|
print("Tool '" + str(self) + "' was not installed. No need to uninstall.")
|
|
return
|
|
print("Uninstalling tool '" + str(self) + "'..")
|
|
if hasattr(self, 'custom_uninstall_script'):
|
|
if self.custom_uninstall_script == 'uninstall_optimizer': uninstall_optimizer(self)
|
|
elif self.custom_uninstall_script == 'uninstall_binaryen': uninstall_binaryen(self)
|
|
else: raise Exception('Unknown custom_uninstall_script directive "' + custom_uninstall_script + '"!')
|
|
try:
|
|
print("Deleting path '" + self.installation_path() + "'")
|
|
remove_tree(self.installation_path())
|
|
os.remove(self.installation_path())
|
|
except:
|
|
pass
|
|
print("Done uninstalling '" + str(self) + "'.")
|
|
|
|
def dependencies(self):
|
|
if not hasattr(self, 'uses'):
|
|
return []
|
|
deps = []
|
|
|
|
for tool_name in self.uses:
|
|
tool = find_tool(tool_name)
|
|
if tool:
|
|
deps += [tool]
|
|
return deps
|
|
|
|
def recursive_dependencies(self):
|
|
if not hasattr(self, 'uses'):
|
|
return []
|
|
deps = []
|
|
for tool_name in self.uses:
|
|
tool = find_tool(tool_name)
|
|
if tool:
|
|
deps += [tool]
|
|
deps += tool.recursive_dependencies()
|
|
return deps
|
|
|
|
# A global registry of all known Emscripten SDK tools available in the SDK manifest.
|
|
tools = []
|
|
|
|
# A global registry of all known SDK toolsets.
|
|
sdks = []
|
|
|
|
# N.B. In both tools and sdks list above, we take the convention that the newest items are at the back of the list (ascending chronological order)
|
|
|
|
def find_tool(name):
|
|
for tool in tools:
|
|
if str(tool) == name:
|
|
return tool
|
|
return None
|
|
|
|
def find_sdk(name):
|
|
for sdk in sdks:
|
|
if str(sdk) == name:
|
|
return sdk
|
|
return None
|
|
|
|
def is_os_64bit(): # http://stackoverflow.com/questions/2208828/detect-64bit-os-windows-in-python
|
|
return platform.machine().endswith('64')
|
|
|
|
def find_latest_32bit_sdk():
|
|
for sdk in reversed(sdks): # Newest SDK is always at the end of the list.
|
|
if not hasattr(sdk, 'bitness') or sdk.bitness == 32: # If no 'bitness' field, it means the SDK is universal.
|
|
if not 'nightly' in sdk.version:
|
|
return sdk
|
|
return None
|
|
|
|
def find_latest_64bit_sdk():
|
|
for sdk in reversed(sdks): # Newest SDK is always at the end of the list.
|
|
if not hasattr(sdk, 'bitness') or sdk.bitness == 64: # If no 'bitness' field, it means the SDK is universal.
|
|
if not 'nightly' in sdk.version:
|
|
return sdk
|
|
return None
|
|
|
|
def find_latest_sdk():
|
|
if is_os_64bit():
|
|
return find_latest_64bit_sdk()
|
|
else:
|
|
return find_latest_32bit_sdk()
|
|
|
|
def find_latest_nightly_32bit_sdk():
|
|
for sdk in reversed(sdks): # Newest SDK is always at the end of the list.
|
|
if not hasattr(sdk, 'bitness') or sdk.bitness == 32: # If no 'bitness' field, it means the SDK is universal.
|
|
if 'nightly' in sdk.version:
|
|
return sdk
|
|
return None
|
|
|
|
def find_latest_nightly_64bit_sdk():
|
|
for sdk in reversed(sdks): # Newest SDK is always at the end of the list.
|
|
if not hasattr(sdk, 'bitness') or sdk.bitness == 64: # If no 'bitness' field, it means the SDK is universal.
|
|
if 'nightly' in sdk.version:
|
|
return sdk
|
|
return None
|
|
|
|
def find_latest_nightly_sdk():
|
|
if is_os_64bit():
|
|
return find_latest_nightly_64bit_sdk()
|
|
else:
|
|
return find_latest_nightly_32bit_sdk()
|
|
|
|
# Finds the best-matching python tool for use.
|
|
def find_used_python():
|
|
for t in reversed(tools): # Find newest tool first - those are always at the end of the list.
|
|
if t.id == 'python' and t.is_installed() and t.is_active() and t.is_env_active():
|
|
return t
|
|
for t in reversed(tools):
|
|
if t.id == 'python' and t.is_installed() and t.is_active():
|
|
return t
|
|
for t in reversed(tools):
|
|
if t.id == 'python' and t.is_installed():
|
|
return t
|
|
return None
|
|
|
|
def is_version_at_least(ver, target):
|
|
ver = ver.split('.')
|
|
target = target.split('.')
|
|
while len(ver) < len(target):
|
|
ver += ['0']
|
|
for i in range(len(ver)):
|
|
if int(ver[i]) < int(target[i]):
|
|
return False
|
|
elif int(ver[i]) > int(target[i]):
|
|
return True
|
|
return True
|
|
|
|
def version_compare(x, y):
|
|
a = 1 if is_version_at_least(x, y) else 0
|
|
b = 1 if is_version_at_least(y, x) else 0
|
|
return a - b
|
|
|
|
def fetch_emscripten_tags():
|
|
git = GIT(must_succeed=False)
|
|
if not git:
|
|
print('Update complete, however skipped fetching the Emscripten tags, since git was not found.')
|
|
if WINDOWS:
|
|
print("If you want to compile one of the tagged releases from source, please install git by typing 'emsdk install git-1.9.4', or alternatively by installing it manually from http://git-scm.com/downloads . If you install git manually, remember to add it to PATH.")
|
|
elif OSX:
|
|
print("If you want to compile one of the tagged releases from source, please install git from http://git-scm.com/ , or by installing XCode and then the XCode Command Line Tools (see http://stackoverflow.com/questions/9329243/xcode-4-4-command-line-tools ).")
|
|
elif LINUX:
|
|
print("If you want to compile one of the tagged releases from source, please install git using your package manager, see http://git-scm.com/book/en/Getting-Started-Installing-Git .")
|
|
else:
|
|
print("If you want to compile one of the tagged releases from source, please install git.")
|
|
print("If you are not looking to build Emscripten from source, you can safely ignore this message.")
|
|
return
|
|
|
|
print('Fetching all tags from Emscripten Github repository...')
|
|
tags = run_get_output([GIT(), 'ls-remote', '--tags', emscripten_git_repo])
|
|
if tags[0] != 0: return False # failed to get tags?
|
|
tags = tags[1]
|
|
all_tags = []
|
|
for t in tags.split('\n'):
|
|
try:
|
|
t = t[t.index('refs/tags/') + len('refs/tags/'):].strip()
|
|
if is_version_at_least(t, '1.28.2'):
|
|
all_tags += [t]
|
|
except:
|
|
pass
|
|
all_tags = sorted(all_tags, cmp=version_compare)
|
|
open(sdk_path('emscripten-tags.txt'), 'w').write('\n'.join(all_tags))
|
|
if len(all_tags) > 0:
|
|
print('Done. ' + str(len(all_tags)) + ' tagged releases available, latest is ' + all_tags[-1] + '.')
|
|
else:
|
|
print('Done. No tagged releases available.')
|
|
|
|
def os_name_for_llvm_location():
|
|
if WINDOWS: return 'win'
|
|
if LINUX: return 'linux'
|
|
if OSX: return 'osx'
|
|
def os_name_for_emscripten_location():
|
|
if WINDOWS: return 'win'
|
|
else: return 'linux'
|
|
|
|
print('Fetching all precompiled Nightly versions..')
|
|
download_file('https://s3.amazonaws.com/mozilla-games/emscripten/packages/llvm/nightly/' + os_name_for_llvm_location() + '_32bit/index.txt', 'llvm-nightlies-32bit.txt', download_even_if_exists=True)
|
|
download_file('https://s3.amazonaws.com/mozilla-games/emscripten/packages/llvm/nightly/' + os_name_for_llvm_location() + '_64bit/index.txt', 'llvm-nightlies-64bit.txt', download_even_if_exists=True)
|
|
download_file('https://s3.amazonaws.com/mozilla-games/emscripten/packages/emscripten/nightly/' + os_name_for_emscripten_location() + '/index.txt', 'emscripten-nightlies.txt', download_even_if_exists=True)
|
|
|
|
def update_emsdk():
|
|
if WINDOWS:
|
|
download_and_unzip(urljoin(emsdk_packages_url, 'emsdk_windows_update.zip'), emsdk_path(), download_even_if_exists=True)
|
|
rmfile('zips/emsdk_windows_update.zip')
|
|
elif OSX or LINUX:
|
|
download_and_unzip(urljoin(emsdk_packages_url, 'emsdk_unix_update.tar.gz'), emsdk_path(), download_even_if_exists=True)
|
|
rmfile('zips/emsdk_unix_update.tar.gz')
|
|
else:
|
|
print('Unsupported OS, cannot update!')
|
|
fetch_emscripten_tags()
|
|
|
|
def load_emscripten_tags():
|
|
try:
|
|
return open(sdk_path('emscripten-tags.txt'), 'r').read().split('\n')
|
|
except:
|
|
return []
|
|
|
|
def remove_prefix(s, prefix):
|
|
if s.startswith(prefix): return s[len(prefix):]
|
|
else: return s
|
|
|
|
def remove_suffix(s, suffix):
|
|
if s.endswith(suffix): return s[:len(s)-len(suffix)]
|
|
else: return s
|
|
|
|
# Kind should be one of: 'llvm-nightlies-32bit.txt', 'llvm-nightlies-64bit.txt', 'emscripten-nightlies.txt'
|
|
def load_nightlies_list(kind):
|
|
try:
|
|
items = open(sdk_path(kind), 'r').read().split('\n')
|
|
items = map(lambda x: remove_suffix(remove_suffix(remove_prefix(remove_prefix(x, 'emscripten-llvm-e'), 'emscripten-nightly-'), '.tar.gz'), '.zip').strip(), items)
|
|
items = filter(lambda x: 'latest' not in x and len(x) > 0, items)
|
|
return items
|
|
except:
|
|
return []
|
|
|
|
def load_llvm_32bit_nightlies():
|
|
return load_nightlies_list('llvm-nightlies-32bit.txt')
|
|
|
|
def load_llvm_64bit_nightlies():
|
|
return load_nightlies_list('llvm-nightlies-64bit.txt')
|
|
|
|
def load_emscripten_nightlies():
|
|
return load_nightlies_list('emscripten-nightlies.txt')
|
|
|
|
def load_sdk_manifest():
|
|
global tools, sdks
|
|
try:
|
|
manifest = json.loads(open(sdk_path("emsdk_manifest.json"), "r").read())
|
|
except Exception as e:
|
|
print('Error parsing emsdk_manifest.json!')
|
|
print(str(e))
|
|
return
|
|
|
|
emscripten_tags = load_emscripten_tags()
|
|
llvm_32bit_nightlies = load_llvm_32bit_nightlies()
|
|
llvm_64bit_nightlies = load_llvm_64bit_nightlies()
|
|
emscripten_nightlies = load_emscripten_nightlies()
|
|
|
|
def dependencies_exist(sdk):
|
|
for tool_name in sdk.uses:
|
|
tool = find_tool(tool_name)
|
|
if not tool: return False
|
|
return True
|
|
|
|
# A 'category parameter' is a %foo%-encoded identifier that specifies
|
|
# a class of tools instead of just one tool, e.g. %tag% or %nightly..%
|
|
def expand_category_param(param, category_list, t, is_sdk):
|
|
global tools, sdks
|
|
for i in range(len(category_list)):
|
|
ver = category_list[i]
|
|
t2 = copy.copy(t)
|
|
for p, v in vars(t2).iteritems():
|
|
if isinstance(v, basestring) and param in v:
|
|
t2.__dict__[p] = v.replace(param, ver)
|
|
t2.is_old = i < len(category_list) - 2
|
|
if hasattr(t2, 'uses'):
|
|
t2.uses = map((lambda x: x.replace(param, ver)), t2.uses)
|
|
if is_sdk:
|
|
if dependencies_exist(t2):
|
|
sdks.append(t2)
|
|
else:
|
|
tools.append(t2)
|
|
|
|
for tool in manifest['tools']:
|
|
t = Tool(tool)
|
|
if t.compatible_with_this_os():
|
|
if not hasattr(t, 'is_old'):
|
|
t.is_old = False
|
|
|
|
# Expand the metapackages that refer to tags or nightlies.
|
|
if '%tag%' in t.version:
|
|
expand_category_param('%tag%', emscripten_tags, t, is_sdk=False)
|
|
elif '%nightly-llvm-64bit%' in t.version:
|
|
expand_category_param('%nightly-llvm-64bit%', llvm_64bit_nightlies, t, is_sdk=False)
|
|
elif '%nightly-llvm-32bit%' in t.version:
|
|
expand_category_param('%nightly-llvm-32bit%', llvm_32bit_nightlies, t, is_sdk=False)
|
|
elif '%nightly-emscripten%' in t.version:
|
|
expand_category_param('%nightly-emscripten%', emscripten_nightlies, t, is_sdk=False)
|
|
else:
|
|
tools.append(t)
|
|
|
|
for sdk_str in manifest['sdks']:
|
|
sdk = Tool(sdk_str)
|
|
sdk.id = "sdk"
|
|
if sdk.compatible_with_this_os():
|
|
if not hasattr(sdk, 'is_old'):
|
|
sdk.is_old = False
|
|
|
|
if '%tag%' in sdk.version:
|
|
expand_category_param('%tag%', emscripten_tags, sdk, is_sdk=True)
|
|
elif '%nightly-llvm-64bit%' in sdk.version:
|
|
expand_category_param('%nightly-llvm-64bit%', llvm_64bit_nightlies, sdk, is_sdk=True)
|
|
elif '%nightly-llvm-32bit%' in sdk.version:
|
|
expand_category_param('%nightly-llvm-32bit%', llvm_32bit_nightlies, sdk, is_sdk=True)
|
|
elif '%nightly-emscripten%' in sdk.version:
|
|
expand_category_param('%nightly-emscripten%', emscripten_nightlies, sdk, is_sdk=True)
|
|
else:
|
|
sdks.append(sdk)
|
|
|
|
# Tests if the two given tools can be active at the same time.
|
|
# Currently only a simple check for name for same tool with different versions,
|
|
# possibly adds more logic in the future.
|
|
def can_simultaneously_activate(tool1, tool2):
|
|
return tool1.id != tool2.id
|
|
|
|
def remove_nonexisting_tools(tool_list, log_errors=True):
|
|
i = 0
|
|
while i < len(tool_list):
|
|
tool = tool_list[i]
|
|
if not tool.is_installed():
|
|
if log_errors:
|
|
print("The SDK/tool '" + str(tool) + "' cannot be activated since it is not installed! Skipping this tool...")
|
|
tool_list.pop(i)
|
|
continue
|
|
i += 1
|
|
return tool_list
|
|
|
|
# Expands dependencies for each tool, and removes ones that don't exist.
|
|
def process_tool_list(tools_to_activate, log_errors=True):
|
|
i = 0
|
|
# Gather dependencies for each tool
|
|
while i < len(tools_to_activate):
|
|
tool = tools_to_activate[i]
|
|
deps = tool.recursive_dependencies()
|
|
tools_to_activate = tools_to_activate[:i] + deps + tools_to_activate[i:]
|
|
i += len(deps)+1
|
|
|
|
tools_to_activate = remove_nonexisting_tools(tools_to_activate, log_errors=log_errors)
|
|
|
|
# Remove conflicting tools
|
|
i = 0
|
|
while i < len(tools_to_activate):
|
|
j = 0
|
|
while j < i:
|
|
secondary_tool = tools_to_activate[j]
|
|
primary_tool = tools_to_activate[i]
|
|
if not can_simultaneously_activate(primary_tool, secondary_tool):
|
|
tools_to_activate.pop(j)
|
|
j -= 1
|
|
i -= 1
|
|
j += 1
|
|
i += 1
|
|
return tools_to_activate
|
|
|
|
# Reconfigure .emscripten to choose the currently activated toolset, set PATH and other environment variables.
|
|
# Returns the full list of deduced tools that are now active.
|
|
def set_active_tools(tools_to_activate, permanently_activate):
|
|
tools_to_activate = process_tool_list(tools_to_activate, log_errors=True)
|
|
|
|
generate_dot_emscripten(tools_to_activate)
|
|
|
|
# Construct a .bat script that will be invoked to set env. vars and PATH
|
|
if WINDOWS:
|
|
env_string = construct_env(tools_to_activate, False)
|
|
open(EMSDK_SET_ENV, 'w').write(env_string)
|
|
|
|
# Apply environment variables to global all users section.
|
|
if WINDOWS and permanently_activate:
|
|
# Individual env. vars
|
|
for tool in tools_to_activate:
|
|
tool.win_activate_env_vars(permanently_activate=True)
|
|
|
|
# PATH variable
|
|
newpath, added_items = adjusted_path(tools_to_activate, system_path_only=True)
|
|
if newpath != os.environ['PATH']: # Are there any actual changes?
|
|
win_set_environment_variable('PATH', newpath, system=True)
|
|
|
|
return tools_to_activate
|
|
|
|
def currently_active_sdk():
|
|
for sdk in reversed(sdks):
|
|
if sdk.is_active():
|
|
return sdk
|
|
return None
|
|
|
|
def currently_active_tools():
|
|
active_tools = []
|
|
for tool in tools:
|
|
if tool.is_active():
|
|
active_tools += [tool]
|
|
return active_tools
|
|
|
|
# http://stackoverflow.com/questions/480214/how-do-you-remove-duplicates-from-a-list-in-python-whilst-preserving-order
|
|
def unique_items(seq):
|
|
seen = set()
|
|
seen_add = seen.add
|
|
return [ x for x in seq if x not in seen and not seen_add(x)]
|
|
|
|
# Tests if a path is contained in the given list, but with separators normalized.
|
|
def normalized_contains(lst, elem):
|
|
elem = to_unix_path(elem)
|
|
for e in lst:
|
|
if elem == to_unix_path(e):
|
|
return True
|
|
return False
|
|
|
|
# Looks at the current PATH and adds and removes entries so that the PATH reflects
|
|
# the set of given active tools.
|
|
def adjusted_path(tools_to_activate, log_additions=False, system_path_only=False):
|
|
# These directories should be added to PATH
|
|
path_add = get_required_path(tools_to_activate)
|
|
# These already exist.
|
|
if WINDOWS:
|
|
existing_path = win_get_environment_variable('PATH', system=True)
|
|
if not system_path_only:
|
|
current_user_path = win_get_environment_variable('PATH', system=False)
|
|
if current_user_path: existing_path += ENVPATH_SEPARATOR + current_user_path
|
|
existing_path = existing_path.split(ENVPATH_SEPARATOR)
|
|
|
|
# Fix up after potential changes made by bug https://github.com/kripken/emscripten/issues/4121
|
|
system_root = os.environ['SystemRoot'].lower()
|
|
for i in range(len(existing_path)):
|
|
p = existing_path[i]
|
|
if p.lower() == system_root: p = '%SystemRoot%'
|
|
elif (system_root + '\\system32') in p.lower(): p = '%SystemRoot%\\system32'
|
|
elif (system_root + '\\system32\\wbem') in p.lower(): p = '%SystemRoot%\\System32\\Wbem'
|
|
elif (system_root + '\\system32\\windowspowershell\v1.0') in p.lower(): p = '%SystemRoot%\\System32\\WindowsPowerShell\v1.0\\'
|
|
existing_path[i] = p
|
|
|
|
else:
|
|
existing_path = os.environ['PATH'].split(ENVPATH_SEPARATOR)
|
|
emsdk_root_path = to_unix_path(emsdk_path())
|
|
|
|
existing_emsdk_tools = [item for item in existing_path if to_unix_path(item).startswith(emsdk_root_path)]
|
|
new_emsdk_tools = [item for item in path_add if not normalized_contains(existing_emsdk_tools, item)]
|
|
# Existing non-emsdk tools
|
|
existing_path = [item for item in existing_path if not to_unix_path(item).startswith(emsdk_root_path)]
|
|
new_path = [item for item in path_add if not normalized_contains(existing_path, item)]
|
|
return (ENVPATH_SEPARATOR.join(unique_items(new_path + existing_path)), new_emsdk_tools)
|
|
|
|
def construct_env(tools_to_activate, permanent):
|
|
global emscripten_config_directory
|
|
env_string = ''
|
|
newpath, added_path = adjusted_path(tools_to_activate)
|
|
|
|
# Dont permanently add to PATH, since this will break the whole system if there are more than 1024 chars in PATH.
|
|
# (SETX truncates to set only 1024 chars)
|
|
# if permanent:
|
|
# print('SETX PATH "' + newpath + '"')
|
|
# else:
|
|
|
|
if os.environ['PATH'] != newpath: # Don't bother setting the path if there are no changes.
|
|
if WINDOWS: env_string += 'SET PATH=' + newpath + '\n'
|
|
else: env_string += 'export PATH="' + newpath + '"\n'
|
|
if len(added_path) > 0:
|
|
print('Adding directories to PATH:')
|
|
for item in added_path:
|
|
print('PATH += ' + item)
|
|
print('')
|
|
|
|
env_vars_to_add = []
|
|
|
|
# A core variable EMSDK points to the root of Emscripten SDK directory.
|
|
env_vars_to_add += [('EMSDK', to_unix_path(emsdk_path()))]
|
|
|
|
em_config_path = os.path.normpath(os.path.join(emscripten_config_directory, '.emscripten'))
|
|
if not 'EM_CONFIG' in os.environ or to_unix_path(os.environ['EM_CONFIG']) != to_unix_path(em_config_path):
|
|
env_vars_to_add += [('EM_CONFIG', em_config_path)]
|
|
if emscripten_config_directory == emsdk_path():
|
|
em_cache_dir = sdk_path('.emscripten_cache')
|
|
if not 'EM_CACHE' in os.environ or to_unix_path(os.environ['EM_CACHE']) != to_unix_path(em_cache_dir):
|
|
env_vars_to_add += [('EM_CACHE', em_cache_dir)]
|
|
mkdir_p(em_cache_dir)
|
|
|
|
for tool in tools_to_activate:
|
|
if hasattr(tool, 'activated_env'):
|
|
(key, value) = parse_key_value(tool.activated_env)
|
|
value = to_native_path(tool.expand_vars(value))
|
|
if not key in os.environ or to_unix_path(os.environ[key]) != to_unix_path(value): # Don't set env. vars which are already set to the correct value.
|
|
env_vars_to_add += [(key, value)]
|
|
|
|
if len(env_vars_to_add) > 0:
|
|
print('Setting environment variables:')
|
|
for key, value in env_vars_to_add:
|
|
if WINDOWS:
|
|
if permanent:
|
|
env_string += 'SETX ' + key + ' "' + value + '"\n'
|
|
else:
|
|
env_string += 'SET ' + key + '=' + value + '\n'
|
|
else:
|
|
env_string += 'export ' + key + '="' + value + '"\n'
|
|
print(key + ' = ' + value)
|
|
print('')
|
|
return env_string
|
|
|
|
def silentremove(filename):
|
|
try:
|
|
os.remove(filename)
|
|
except OSError as e:
|
|
if e.errno != errno.ENOENT: raise
|
|
|
|
def main():
|
|
global emscripten_config_directory, BUILD_FOR_TESTING, ENABLE_LLVM_ASSERTIONS
|
|
load_dot_emscripten()
|
|
load_sdk_manifest()
|
|
|
|
if len(sys.argv) <= 1 or sys.argv[1] == 'help' or sys.argv[1] == '--help':
|
|
if len(sys.argv) <= 1:
|
|
print(' emsdk: No command given. Please call one of the following:')
|
|
else:
|
|
print(' emsdk: Available commands:')
|
|
|
|
print('''
|
|
emsdk list [--old] - Lists all available SDKs and tools and their
|
|
current installation status. With the --old
|
|
parameter, also historical versions are
|
|
shown.
|
|
|
|
emsdk update - Fetches a list of updates from the net (but
|
|
does not install them)
|
|
|
|
emsdk install [options] <tool 1> <tool 2> <tool 3> ...
|
|
- Downloads and installs given tools or SDKs.
|
|
Options can contain:
|
|
|
|
-j<num>: Specifies the number of cores to use when
|
|
building the tool. Default: use one less
|
|
than the # of detected cores.
|
|
|
|
--build=<type>: Controls what kind of build of LLVM to
|
|
perform. Pass either 'Debug', 'Release',
|
|
'MinSizeRel' or 'RelWithDebInfo'. Default:
|
|
'Release' for LLVM master branch, and
|
|
'RelWithDebInfo' for LLVM incoming branch.
|
|
|
|
--shallow: When installing tools from one of the git
|
|
development branches 'master' or 'incoming',
|
|
this parameter can be passed to perform a
|
|
shallow git clone instead of a full one.
|
|
This reduces the amount of network transfer
|
|
that is needed. This option should only be
|
|
used when you are interested in downloading
|
|
one of the development branches, but are not
|
|
looking to develop Emscripten yourself.
|
|
Default: disabled, i.e. do a full clone.
|
|
|
|
--build-tests: If enabled, LLVM is built with internal tests
|
|
included. Pass this to enable running test
|
|
other.test_llvm_lit in the Emscripten test
|
|
suite. Default: disabled.
|
|
--enable-assertions: If specified, LLVM is built with assert()
|
|
checks enabled. Useful for development
|
|
purposes. Default: Enabled for 'incoming'
|
|
branch, disabled for 'master' branch.
|
|
--disable-assertions: Forces assertions off during the build.
|
|
|
|
--vs2013/--vs2015: If building from source, overrides to build
|
|
using the specified compiler. When installing
|
|
precompiled packages, this has no effect.
|
|
Note: The same compiler specifier must be
|
|
passed to the emsdk activate command to
|
|
activate the desired version.
|
|
|
|
emsdk uninstall <tool/sdk> - Removes the given tool or SDK from disk.''')
|
|
|
|
if WINDOWS:
|
|
print('''
|
|
emsdk activate [--global] [--embedded] [--build=type] [--vs2013/--vs2015] <tool/sdk>
|
|
|
|
- Activates the given tool or SDK in the
|
|
environment of the current shell. If the
|
|
--global option is passed, the registration
|
|
is done globally to all users in the system
|
|
environment. If the --embedded option is
|
|
passed, all Emcripten configuration files as
|
|
well as the temp, cache and ports directories
|
|
are located inside the Emscripten SDK
|
|
directory rather than the user home
|
|
directory. If a custom compiler version was
|
|
used to override the compiler to use, pass
|
|
the same --vs2013/--vs2015 parameter here to
|
|
choose which version to activate.
|
|
|
|
emcmdprompt.bat - Spawns a new command prompt window with the
|
|
Emscripten environment active.''')
|
|
else:
|
|
print(''' emsdk activate [--embedded] [--build=type] <tool/sdk>
|
|
|
|
- Activates the given tool or SDK in the
|
|
environment of the current shell. If the
|
|
--embedded option is passed, all Emcripten
|
|
configuration files as well as the temp, cache
|
|
and ports directories are located inside the
|
|
Emscripten SDK directory rather than the user
|
|
home directory.''')
|
|
|
|
print('''
|
|
Both commands 'install' and 'activate' accept an optional parameter
|
|
'--build=type', which can be used to override what kind of installation
|
|
or activation to perform. Possible values for type are Debug, Release,
|
|
MinSizeRel or RelWithDebInfo. Note: When overriding a custom build type,
|
|
be sure to match the same --build= option to both 'install' and
|
|
'activate' commands and the invocation of 'emsdk_env', or otherwise
|
|
these commands will default to operating on the default build types,
|
|
which are Release for the 'master' SDK, and RelWithDebInfo for the
|
|
'incoming' SDK.''')
|
|
return 1
|
|
cmd = sys.argv[1]
|
|
|
|
# Process global args
|
|
for i in range(2, len(sys.argv)):
|
|
if sys.argv[i].startswith('--build='):
|
|
build_type = re.match(r'^--build=(.+)$', sys.argv[i])
|
|
if build_type:
|
|
global CMAKE_BUILD_TYPE_OVERRIDE
|
|
build_type = build_type.group(1)
|
|
build_types = ['Debug', 'MinSizeRel', 'RelWithDebInfo', 'Release']
|
|
try:
|
|
build_type_index = [x.lower() for x in build_types].index(build_type.lower())
|
|
CMAKE_BUILD_TYPE_OVERRIDE = build_types[build_type_index]
|
|
sys.argv[i] = ''
|
|
except:
|
|
print('Unknown CMake build type "' + build_type + '" specified! Please specify one of ' + str(build_types), file=sys.stderr)
|
|
return 1
|
|
else:
|
|
print("Invalid command line parameter " + sys.argv[i] + ' specified!', file=sys.stderr)
|
|
return 1
|
|
sys.argv = [x for x in sys.argv if not len(x) == 0]
|
|
|
|
# Replace meta-packages with the real package names.
|
|
if (cmd == 'update' or cmd == 'install' or cmd == 'activate'):
|
|
for i in range(2, len(sys.argv)):
|
|
if sys.argv[i] == 'latest':
|
|
sys.argv[i] = str(find_latest_sdk())
|
|
elif sys.argv[i] == 'latest-32bit':
|
|
sys.argv[i] = str(find_latest_32bit_sdk())
|
|
elif sys.argv[i] == 'latest-64bit':
|
|
sys.argv[i] = str(find_latest_64bit_sdk())
|
|
elif sys.argv[i] == 'sdk-nightly-latest':
|
|
sys.argv[i] = str(find_latest_nightly_sdk())
|
|
elif sys.argv[i] == 'sdk-nightly-latest-32bit':
|
|
sys.argv[i] = str(find_latest_nightly_32bit_sdk())
|
|
elif sys.argv[i] == 'sdk-nightly-latest-64bit':
|
|
sys.argv[i] = str(find_latest_nightly_64bit_sdk())
|
|
|
|
if cmd == 'list':
|
|
print('')
|
|
show_old = len(sys.argv) >= 3 and sys.argv[2] == '--old'
|
|
has_partially_active_tools = [False] # Use array to work around the lack of being able to mutate from enclosing function.
|
|
if len(tools) > 0:
|
|
def find_tools(needs_compilation):
|
|
t = []
|
|
for tool in tools:
|
|
if tool.is_old and not show_old:
|
|
continue
|
|
if tool.needs_compilation() != needs_compilation:
|
|
continue
|
|
t += [tool]
|
|
return t
|
|
|
|
def print_tools(t):
|
|
for tool in t:
|
|
if tool.is_old and not show_old:
|
|
continue
|
|
if tool.can_be_installed() == True:
|
|
installed = '\tINSTALLED' if tool.is_installed() else ''
|
|
else:
|
|
installed = '\tNot available: ' + tool.can_be_installed()
|
|
tool_is_active = tool.is_active()
|
|
tool_is_env_active = tool_is_active and tool.is_env_active()
|
|
if tool_is_env_active: active = ' * '
|
|
elif tool_is_active:
|
|
active = '(*)'
|
|
has_partially_active_tools[0] = has_partially_active_tools[0] or True
|
|
else: active = ' '
|
|
print(' ' + active + ' {0: <25}'.format(str(tool)) + installed)
|
|
print('')
|
|
|
|
print('The following precompiled tool packages are available for download:')
|
|
print_tools(find_tools(False))
|
|
print('The following tools can be compiled from source:')
|
|
print_tools(find_tools(True))
|
|
else:
|
|
print("There are no tools available. Run 'emsdk update' to fetch the latest set of tools.")
|
|
print('')
|
|
|
|
if len(sdks) > 0:
|
|
def find_sdks(needs_compilation):
|
|
s = []
|
|
for sdk in sdks:
|
|
if sdk.is_old and not show_old:
|
|
continue
|
|
if sdk.needs_compilation() == needs_compilation:
|
|
s += [sdk]
|
|
return s
|
|
|
|
def print_sdks(s):
|
|
for sdk in s:
|
|
installed = '\tINSTALLED' if sdk.is_installed() else ''
|
|
active = '*' if sdk.is_active() else ' '
|
|
print(' ' + active + ' {0: <25}'.format(str(sdk)) + installed)
|
|
print('')
|
|
print('The following precompiled SDKs are available for download: (Run "./emsdk update" to pull in the latest list)')
|
|
print_sdks(find_sdks(False))
|
|
print('The following SDKs can be compiled from source:')
|
|
print_sdks(find_sdks(True))
|
|
|
|
print('Items marked with * are activated for the current user.')
|
|
if has_partially_active_tools[0]:
|
|
env_cmd = 'emsdk_env.bat' if WINDOWS else 'source ./emsdk_env.sh'
|
|
print('Items marked with (*) are selected for use, but your current shell environment is not configured to use them. Type "' + env_cmd + '" to set up your current shell to use them' + (', or call "emsdk activate --global <name_of_sdk>" to permanently activate them.' if WINDOWS else '.'))
|
|
if not show_old:
|
|
print('')
|
|
print("To access the historical archived versions, type 'emsdk list --old'")
|
|
|
|
return 0
|
|
elif cmd == 'construct_env':
|
|
silentremove(EMSDK_SET_ENV) # Clean up old temp file up front, in case of failure later before we get to write out the new one.
|
|
tools_to_activate = currently_active_tools()
|
|
tools_to_activate = process_tool_list(tools_to_activate, log_errors=True)
|
|
env_string = construct_env(tools_to_activate, len(sys.argv) >= 3 and 'perm' in sys.argv[2])
|
|
open(EMSDK_SET_ENV, 'w').write(env_string)
|
|
if LINUX or OSX: os.chmod(EMSDK_SET_ENV, 0o755)
|
|
return 0
|
|
elif cmd == 'update':
|
|
update_emsdk()
|
|
silentremove(sdk_path(EMSDK_SET_ENV)) # Clean up litter after old emsdk update which may have left this temp file around.
|
|
return 0
|
|
elif cmd == 'activate':
|
|
permanently_activate = '--global' in sys.argv
|
|
if permanently_activate:
|
|
print('Registering active Emscripten environment globally for all users.')
|
|
print('')
|
|
embed_activation_to_sdk_dir = '--embedded' in sys.argv
|
|
if embed_activation_to_sdk_dir:
|
|
# Activating the emsdk tools locally relative to Emscripten SDK directory.
|
|
emscripten_config_directory = emsdk_path()
|
|
print('Writing .emscripten configuration file to Emscripten SDK directory ' + emscripten_config_directory)
|
|
else:
|
|
print('Writing .emscripten configuration file to user home directory ' + emscripten_config_directory)
|
|
# Remove .emscripten from emsdk dir, since its presence is used to detect whether emsdk is activate in embedded mode or not.
|
|
try:
|
|
os.remove(os.path.join(emsdk_path(), ".emscripten"))
|
|
except:
|
|
pass
|
|
|
|
sys.argv = [x for x in sys.argv if not x.startswith('--')]
|
|
# If called without arguments, activate latest versions of all tools
|
|
if len(sys.argv) <= 2:
|
|
tools_to_activate = list(tools)
|
|
tools_to_activate = remove_nonexisting_tools(tools_to_activate, log_errors=False)
|
|
else:
|
|
tools_to_activate = currently_active_tools()
|
|
for i in range(2, len(sys.argv)):
|
|
tool = find_tool(sys.argv[i])
|
|
if tool == None:
|
|
tool = find_sdk(sys.argv[i])
|
|
if tool == None:
|
|
print("Error: No tool or SDK found by name '" + sys.argv[i] + "'.")
|
|
return 1
|
|
tools_to_activate += [tool]
|
|
tools_to_activate = set_active_tools(tools_to_activate, permanently_activate=permanently_activate)
|
|
if WINDOWS and not permanently_activate:
|
|
print('The changes made to environment variables only apply to the currently running shell instance. Use the \'emsdk_env.bat\' to re-enter this environment later, or if you\'d like to permanently register this environment globally to all users in Windows Registry, rerun this command with the option --global.')
|
|
return 0
|
|
elif cmd == 'install':
|
|
# Process args
|
|
for i in range(2, len(sys.argv)):
|
|
if sys.argv[i].startswith('-j'):
|
|
multicore = re.match(r'^-j(\d+)$', sys.argv[i])
|
|
if multicore:
|
|
global CPU_CORES
|
|
CPU_CORES = int(multicore.group(1))
|
|
sys.argv[i] = ''
|
|
else:
|
|
print("Invalid command line parameter " + sys.argv[i] + ' specified!', file=sys.stderr)
|
|
return 1
|
|
elif sys.argv[i] == '--shallow':
|
|
global GIT_CLONE_SHALLOW
|
|
GIT_CLONE_SHALLOW = True
|
|
sys.argv[i] = ''
|
|
elif sys.argv[i] == '--build-tests':
|
|
BUILD_FOR_TESTING = True
|
|
sys.argv[i] = ''
|
|
elif sys.argv[i] == '--enable-assertions':
|
|
ENABLE_LLVM_ASSERTIONS = 'ON'
|
|
sys.argv[i] = ''
|
|
elif sys.argv[i] == '--disable-assertions':
|
|
ENABLE_LLVM_ASSERTIONS = 'OFF'
|
|
sys.argv[i] = ''
|
|
sys.argv = [x for x in sys.argv if not len(x) == 0]
|
|
if len(sys.argv) <= 2:
|
|
print("Missing parameter. Type 'emsdk install <tool name>' to install a tool or an SDK. Type 'emsdk list' to obtain a list of available tools. Type 'emsdk install latest' to automatically install the newest version of the SDK.")
|
|
return 1
|
|
for t in sys.argv[2:]:
|
|
tool = find_tool(t)
|
|
if tool == None:
|
|
tool = find_sdk(t)
|
|
if tool == None:
|
|
print("Error: No tool or SDK found by name '" + t + "'.")
|
|
return 1
|
|
success = tool.install()
|
|
if not success:
|
|
return 1
|
|
elif cmd == 'uninstall':
|
|
if len(sys.argv) <= 2:
|
|
print("Syntax error. Call 'emsdk uninstall <tool name>'. Call 'emsdk list' to obtain a list of available tools.")
|
|
return 1
|
|
tool = find_tool(sys.argv[2])
|
|
if tool == None:
|
|
print("Error: Tool by name '" + sys.argv[2] + "' was not found.")
|
|
return 1
|
|
tool.uninstall()
|
|
else:
|
|
print("Unknown command '" + cmd + "' given! Type 'emsdk help' to get a list of commands.")
|
|
return 1
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|