#!/usr/bin/env python import sys, optparse, subprocess, urllib2, os, os.path, errno, zipfile, string, json, platform, shutil, tarfile, urlparse, tempfile, multiprocessing # 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 = urlparse.urljoin(emsdk_master_server, 'packages/') emscripten_git_repo = 'git@github.com:kripken/emscripten.git' 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 = ':' emscripten_config_directory = os.path.expanduser("~/") EMSDK_SET_ENV = 'emsdk_set_env.bat' if WINDOWS else 'emsdk_set_env.sh' CMAKE_GENERATOR = 'Unix Makefiles' if WINDOWS: build_with_visualstudio = True if build_with_visualstudio: CMAKE_GENERATOR = 'Visual Studio 12' else: CMAKE_GENERATOR += 'MinGW Makefiles' 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, e: if e[0] != 2: # 'The system cannot find the file specified.' print >> sys.stderr, 'Failed to read environment variable ' + key + ':' print >> sys.stderr, str(e) 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): # print >> sys.stderr, 'set ' + str(key) + '=' + str(value) + ', in system=' + str(system) previous_value = win_get_environment_variable(key, system) if previous_value == value: return # No need to elevate UAC for nothing to set the same value, skip. if not value: try: if system: value = subprocess.call(['REG', 'DELETE', 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', '/V', key, '/f'], stdout=subprocess.PIPE) else: value = subprocess.call(['REG', 'DELETE', 'HKCU\\Environment', '/V', key, '/f'], stdout=subprocess.PIPE) except Exception, e: return return try: if system: cmd = ['bin/elevate.exe', 'SETX', '/M'] else: cmd = ['SETX'] retcode = subprocess.call(cmd + [key, value], stdout=subprocess.PIPE) if retcode is not 0: print >> sys.stderr, 'ERROR! Failed to set environment variable ' + key + '=' + value + '. You may need to set it manually.' except Exception, e: print >> sys.stderr, 'ERROR! Failed to set environment variable ' + key + '=' + value + ':' print >> sys.stderr, str(e) print >> sys.stderr, 'You may need to set it manually.' def win_delete_environment_variable(key, system=True): 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)) def emsdk_path(): return to_unix_path(os.path.dirname(os.path.realpath(__file__))) # 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): 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 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): 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 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 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): shutil.rmtree(dest_dir) except: pass shutil.copytree(os.path.join(unzip_to_dir, common_subdir), dest_dir) try: shutil.rmtree(unzip_to_dir) except: pass except zipfile.BadZipfile, e: print "Unzipping file '" + source_filename + "' failed due to reason: " + str(e) + "! Removing the corrupted zip file." rmfile(source_filename) return False except Exception, 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': # 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): file_name = 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 = urllib2.urlopen(url) mkdir_p(os.path.dirname(file_name)) with open(file_name, 'wb') as f: meta = u.info() file_size = int(meta.getheaders("Content-Length")[0]) print "Downloading: %s from %s Bytes: %s" % (file_name, url, file_size) 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) status = r"%10d [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size) status = status + chr(8)*(len(status)+1) print status, except urllib2.HTTPError, e: print "HTTP error with URL '" + url + "': " + str(e) rmfile(file_name) return None except Exception, 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 = download_file(url, dstpath, download_even_if_exists) fix_lineendings(os.path.join(emsdk_path(), filename)) def run_get_output(cmd, cwd=None): process = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, env=os.environ.copy()) (stdout, stderr) = process.communicate() return (process.returncode, stdout, stderr) # 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 # 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 os.path.isdir(os.path.join(dstpath, '.git')): print "Repository '" + url + "' already cloned to directory '" + dstpath + "', skipping." return True mkdir_p(dstpath) return run([GIT(), 'clone', url, dstpath]) == 0 def git_checkout_and_pull(repo_path, branch): run([GIT(), 'fetch', 'origin'], repo_path) try: print "Fetching latest changes to the branch '" + branch + "' for '" + repo_path + "'..." run([GIT(), 'fetch', 'origin'], repo_path) # run([GIT, 'checkout', '-b', branch, '--track', 'origin/'+branch], repo_path) 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. 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 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): success = git_clone(url, dstpath) if not success: return False success = git_checkout_and_pull(dstpath, branch) return success # 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 == 'MinGW Makefiles': generator_suffix = '_mingw' bitness_suffix = '_32' if tool.bitness == 32 else '_64' build_dir = 'build_' + tool.git_branch + generator_suffix + bitness_suffix return build_dir # The directory where the binaries are produced. 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\\RelWithDebInfo') new_llvm_bin_dir = os.path.join(build_dir, 'RelWithDebInfo\\bin') if os.path.exists(os.path.join(tool.install_path, new_llvm_bin_dir)): return new_llvm_bin_dir if os.path.exists(os.path.join(tool.install_path, old_llvm_bin_dir)): return old_llvm_bin_dir return new_llvm_bin_dir else: return os.path.join(build_dir, 'bin') make_env = os.environ.copy() def find_msbuild(sln_file): 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')] contents = open(sln_file, 'r').read() if '# Visual Studio Express 2013' in contents or '# Visual Studio 2013' in contents: search_paths = search_paths_vs2013 + search_paths_old make_env['VCTargetsPath'] = os.path.join(os.environ['ProgramFiles(x86)'], 'MSBuild/Microsoft.Cpp/v4.0/V120') else: search_paths = search_paths_old + search_paths_vs2013 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): cpu_cores = max(multiprocessing.cpu_count()-1, 1) # Don't saturate all cores to not steal the whole system, but be aggressive. if cpu_cores > 1: print 'Performing a parallel build with ' + str(cpu_cores) + ' cores.' else: print 'Performing a singlethreaded build.' if WINDOWS: if 'Visual Studio' in CMAKE_GENERATOR: solution_name = subprocess.check_output(['dir', '/b', '*.sln'], shell=True, cwd=build_root).strip() 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] 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=make_env) if ret != 0: print >> sys.stderr, 'Build failed with exit code ' + ret + '!' print >> sys.stderr, 'Working directory: ' + build_root return False except Exception, e: print >> sys.stderr, 'Build failed due to exception!' print >> sys.stderr, 'Working directory: ' + build_root print >> sys.stderr, str(e) return False return True def cmake_configure(generator, build_root, src_root, build_type, 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] + extra_cmake_args + [src_root] print 'Running CMake: ' + str(cmdline) ret = subprocess.check_call(cmdline, cwd=build_root) if ret != 0: print >> sys.stderr, 'CMake invocation failed with exit code ' + ret + '!' print >> sys.stderr, 'Working directory: ' + build_root return False except OSError, e: if e.errno == errno.ENOENT: print >> sys.stderr, str(e) print >> sys.stderr, 'Could not run CMake, perhaps it has not been installed?' if WINDOWS: print >> sys.stderr, 'Installing this package requires CMake. Get it from http://www.cmake.org/' elif LINUX: print >> sys.stderr, '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/' elif OSX: print >> sys.stderr, '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/' return False raise except Exception, e: print >> sys.stderr, 'CMake invocation failed due to exception!' print >> sys.stderr, 'Working directory: ' + build_root print >> sys.stderr, str(e) return False return True def build_fastcomp_tool(tool): fastcomp_root = tool.installation_path() fastcomp_src_root = os.path.join(fastcomp_root, 'src') git_clone_checkout_and_pull(tool.url, fastcomp_src_root, tool.git_branch) clang_root = os.path.join(fastcomp_src_root, 'tools/clang') git_clone_checkout_and_pull(tool.clang_url, clang_root, tool.git_branch) 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 = tool.cmake_build_type # Configure success = cmake_configure(cmake_generator, build_root, fastcomp_src_root, build_type, ['-DLLVM_TARGETS_TO_BUILD=X86;JSBackend', '-DLLVM_INCLUDE_EXAMPLES=OFF', '-DLLVM_INCLUDE_TESTS=OFF', '-DCLANG_INCLUDE_EXAMPLES=OFF', '-DCLANG_INCLUDE_TESTS=OFF']) if not success: return False # Make success = make_build(build_root, build_type) return success def build_optimizer_tool(tool): src_root = os.path.join(tool.installation_path(), 'tools', 'optimizer') build_root = os.path.join(tool.installation_path(), 'tools', 'optimizer_build') build_type = tool.cmake_build_type # Configure success = cmake_configure(None, build_root, src_root, build_type) if not success: return False # Make success = make_build(build_root, build_type) return success def download_and_unzip(zipfile, dest_dir, download_even_if_exists=False): 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(urlparse.urljoin(emsdk_packages_url, zipfile), 'zips/', download_even_if_exists) 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_unix_path(p): return p.replace('\\', '/') 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) else: temp_dir = tempfile.gettempdir().replace('\\', '/') cfg = 'import os\n' cfg += '''SPIDERMONKEY_ENGINE = '' NODE_JS = 'node' ''' for tool in active_tools: tool_cfg = tool.activated_config() if tool_cfg: tool_cfg = tool_cfg.replace(';', '\n') cfg += tool_cfg + '\n' cfg += '''V8_ENGINE = '' TEMP_DIR = ''' + "'" + temp_dir + "'" + ''' COMPILER_ENGINE = NODE_JS JS_ENGINES = [NODE_JS] ''' with open(dot_emscripten_path(), "w") as text_file: text_file.write(cfg) # Clear old cached emscripten content. try: shutil.rmtree(os.path.join(emscripten_config_directory, ".emscripten_cache"), ignore_errors=True) 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 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%' in str: str = str.replace('%cmake_build_type%', (self.cmake_build_type + '/') if WINDOWS else '') if '%installation_dir%' in str: str = str.replace('%installation_dir%', sdk_path(self.installation_dir())) 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 # 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'): 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 (OSX and 'osx' 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'): return True if OSX and hasattr(self, 'osx_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 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) # print 'activated cfg ' + key + ', value: ' + value # if dot_emscripten.has_key(key): # print 'dot_emscripten ' + dot_emscripten[key] if dot_emscripten.has_key(key) and 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 (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 print "Installing '" + str(self) + "'.." if self.id == 'sdk': 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." return True else: 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) if success: if hasattr(self, 'custom_install_script') and self.custom_install_script == 'build_optimizer': success = build_optimizer_tool(self) 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) else: dst_file = download_file(urlparse.urljoin(emsdk_packages_url, self.download_url()), self.installation_path()) if dst_file: success = True else: success = False if not success: print "Installation failed!" return False print "Done." # 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) + "'.." try: print "Deleting path '" + self.installation_path() + "'" shutil.rmtree(self.installation_path(), ignore_errors=True) os.remove(self.installation_path()) except: pass print "Done." 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. 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. return sdk return None def find_latest_sdk(): if is_os_64bit(): return find_latest_64bit_sdk() else: return find_latest_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 update_emsdk(): if WINDOWS: download_and_unzip(urlparse.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(urlparse.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!' def load_sdk_manifest(): global tools try: manifest = json.loads(open(sdk_path("emsdk_manifest.json"), "r").read()) except Exception, e: print 'Error parsing emsdk_manifest.json!' print str(e) return for tool in manifest['tools']: t = Tool(tool) if t.compatible_with_this_os(): if not hasattr(t, 'is_old'): t.is_old = False 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 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) if newpath != os.environ['PATH']: # Are there any actual changes? if len(newpath) < 1024: win_set_environment_variable('PATH', newpath, system=True) else: print >> sys.stderr, 'ERROR! The new PATH is more than 1024 characters long! A PATH this long cannot be set via command line: please add the PATHs specified above to system PATH manually via Control Panel.' 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): # These directories should be added to PATH path_add = get_required_path(tools_to_activate) # These already exist. 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(existing_path + new_path)), new_emsdk_tools) def construct_env_windows(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. env_string += 'SET 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 = [] 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 permanent: env_string += 'SETX ' + key + ' "' + value + '"\n' else: env_string += 'SET ' + key + '=' + value + '\n' print key + ' = ' + value print '' return env_string def construct_env_unix(tools_to_activate): env_string = '' newpath, added_path = adjusted_path(tools_to_activate) if os.environ['PATH'] != newpath: # Don't bother setting the path if there are no changes. 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 = [] 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: env_string += 'export ' + key + '="' + value + '"\n' print key + ' = ' + value print '' return env_string def construct_env(tools_to_activate, permanent): if WINDOWS: return construct_env_windows(tools_to_activate, permanent) else: return construct_env_unix(tools_to_activate) def silentremove(filename): try: os.remove(filename) except OSError, e: if e.errno != errno.ENOENT: raise def main(): global emscripten_config_directory load_dot_emscripten() load_sdk_manifest() usage_str = "usage: %prog --param1 .. --paramn " # parser = optparse.OptionParser(usage=usage_str) # parser.add_option('update', dest='update', action='store_true', default=False, help='Downloads and installs the latest Emscripten SDK components.') # (options, args) = parser.parse_args(sys.argv) if len(sys.argv) <= 1 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 - Downloads and installs the given tool or SDK. emsdk uninstall - Removes the given tool or SDK from disk. emsdk activate - Activates the given tool or SDK in the environment of the current shell.''' if WINDOWS: print ''' emsdk activate [--global] [--embedded] - 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. emcmdprompt.bat - Spawns a new command prompt window with the Emscripten environment active.''' else: print ''' emsdk activate [--embedded] - 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.''' return 1 cmd = sys.argv[1] if (cmd == 'update' or cmd == 'install' or cmd == 'activate') and len(sys.argv) >= 3: if sys.argv[2] == 'latest': sys.argv[2] = str(find_latest_sdk()) elif sys.argv[2] == 'latest-32bit': sys.argv[2] = str(find_latest_32bit_sdk()) elif sys.argv[2] == 'latest-64bit': sys.argv[2] = str(find_latest_64bit_sdk()) if cmd == 'list': print '' show_old = len(sys.argv) >= 3 and sys.argv[2] == '--old' has_partially_active_tools = False if len(tools) > 0: print 'The following individual tools exist:' for tool in tools: 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 = True else: active = ' ' print ' ' + active + ' {0: <25}'.format(str(tool)) + installed print '' else: print "There are no tools available. Run 'emsdk update' to fetch the latest set of tools." print '' if len(sdks) > 0: print 'The following Emscripten SDK versions are available:' for sdk in sdks: if sdk.is_old and not show_old: continue installed = '\tINSTALLED' if sdk.is_installed() else '' active = '*' if sdk.is_active() else ' ' print ' ' + active + ' {0: <25}'.format(str(sdk)) + installed print '' print 'Items marked with * are activated for the current user.' if has_partially_active_tools: 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 " to permanently activate them.' if not show_old: print '' print "To access the historical archived versions, type 'emsdk list --old'" return 0 elif cmd == 'construct_env': # 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() 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, 0755) 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 = filter(lambda x: not x.startswith('--'), sys.argv) # 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 'To permanently register this environment globally to all users in Windows Registry, rerun the command with the option --global.' return 0 elif cmd == 'install': if len(sys.argv) <= 2: print "Missing parameter. Type 'emsdk install ' 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 tool = find_tool(sys.argv[2]) if tool == None: tool = find_sdk(sys.argv[2]) if tool == None: print "Error: No tool or SDK found by name '" + sys.argv[2] + "'." return 1 success = tool.install() if not success: return 1 elif cmd == 'uninstall': if len(sys.argv) <= 2: print "Syntax error. Call 'emsdk uninstall '. 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())