| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| |
| # Copyright 2015 WebAssembly Community Group participants |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import argparse |
| import glob |
| import json |
| import multiprocessing |
| import os |
| import shutil |
| import sys |
| import tarfile |
| import tempfile |
| import textwrap |
| import time |
| import traceback |
| import zipfile |
| |
| import buildbot |
| import cloud |
| import compile_torture_tests |
| import execute_files |
| from file_util import Chdir, CopyTree, Mkdir, Remove |
| import host_toolchains |
| import link_assembly_files |
| import proc |
| import testing |
| import work_dirs |
| from urllib.request import urlopen, URLError |
| |
| SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) |
| JSVU_OUT_DIR = os.path.expanduser(os.path.join('~', '.jsvu')) |
| |
| # This file has a special path to avoid warnings about the system being unknown |
| CMAKE_TOOLCHAIN_FILE = 'Wasi.cmake' |
| |
| EMSCRIPTEN_CONFIG_UPSTREAM = 'emscripten_config_upstream' |
| |
| # Avoid flakes: use cached repositories to avoid relying on external network. |
| GIT_MIRROR_BASE = 'https://chromium.googlesource.com/' |
| GITHUB_MIRROR_BASE = GIT_MIRROR_BASE + 'external/github.com/' |
| WASM_GIT_BASE = GITHUB_MIRROR_BASE + 'WebAssembly/' |
| EMSCRIPTEN_GIT_BASE = 'https://github.com/emscripten-core/' |
| LLVM_GIT_BASE = 'https://github.com/llvm/' |
| |
| # Name of remote for build script to use. Don't touch origin to avoid |
| # clobbering any local development. |
| WATERFALL_REMOTE = '_waterfall' |
| |
| WASM_STORAGE_BASE = 'https://wasm.storage.googleapis.com/' |
| |
| GNUWIN32_ZIP = 'gnuwin32.zip' |
| |
| # This version is the current LLVM version in development. This needs to be |
| # manually updated to the latest x.0.0 version whenever LLVM starts development |
| # on a new major version. This is so our manual build of compiler-rt is put |
| # where LLVM expects it. |
| LLVM_VERSION = '12.0.0' |
| |
| # Update this number each time you want to create a clobber build. If the |
| # clobber_version.txt file in the build dir doesn't match we remove ALL work |
| # dirs. This works like a simpler version of chromium's landmine feature. |
| CLOBBER_BUILD_TAG = 21 |
| |
| V8_BUILD_SUBDIR = os.path.join('out.gn', 'x64.release') |
| |
| LINUX_SYSROOT = 'sysroot_debian_stretch_amd64' |
| LINUX_SYSROOT_URL = WASM_STORAGE_BASE + LINUX_SYSROOT + '_v2.tar.xz' |
| |
| options = None |
| |
| |
| def GccTestDir(): |
| return GetSrcDir('gcc', 'gcc', 'testsuite') |
| |
| |
| def GetBuildDir(*args): |
| return os.path.join(work_dirs.GetBuild(), *args) |
| |
| |
| def GetPrebuilt(*args): |
| return os.path.join(work_dirs.GetPrebuilt(), *args) |
| |
| |
| def GetPrebuiltClang(binary): |
| return os.path.join(work_dirs.GetV8(), 'third_party', 'llvm-build', |
| 'Release+Asserts', 'bin', binary) |
| |
| |
| def GetSrcDir(*args): |
| return os.path.join(work_dirs.GetSync(), *args) |
| |
| |
| def GetInstallDir(*args): |
| return os.path.join(work_dirs.GetInstall(), *args) |
| |
| |
| def GetTestDir(*args): |
| return os.path.join(work_dirs.GetTest(), *args) |
| |
| |
| def GetLLVMSrcDir(*args): |
| return GetSrcDir('llvm-project', *args) |
| |
| |
| def IsWindows(): |
| return sys.platform == 'win32' |
| |
| |
| def IsLinux(): |
| return sys.platform.startswith('linux') |
| |
| |
| def IsMac(): |
| return sys.platform == 'darwin' |
| |
| |
| def Executable(name, extension='.exe'): |
| return name + extension if IsWindows() else name |
| |
| |
| def WindowsFSEscape(path): |
| return os.path.normpath(path).replace('\\', '/') |
| |
| |
| # Use prebuilt Node.js because the buildbots don't have node preinstalled |
| NODE_VERSION = '12.18.1' |
| NODE_BASE_NAME = 'node-v' + NODE_VERSION + '-' |
| |
| |
| def NodePlatformName(): |
| return { |
| 'darwin': 'darwin-x64', |
| 'linux': 'linux-x64', |
| 'linux2': 'linux-x64', |
| 'win32': 'win-x64' |
| }[sys.platform] |
| |
| |
| def NodeBinDir(): |
| node_subdir = NODE_BASE_NAME + NodePlatformName() |
| if IsWindows(): |
| return GetPrebuilt(node_subdir) |
| return GetPrebuilt(node_subdir, 'bin') |
| |
| |
| def NodeBin(): |
| return Executable(os.path.join(NodeBinDir(), 'node')) |
| |
| |
| def CMakePlatformName(): |
| return { |
| 'linux': 'Linux', |
| 'linux2': 'Linux', |
| 'darwin': 'Darwin', |
| 'win32': 'win64' |
| }[sys.platform] |
| |
| |
| def CMakeArch(): |
| return 'x64' if IsWindows() else 'x86_64' |
| |
| |
| PREBUILT_CMAKE_VERSION = '3.15.3' |
| PREBUILT_CMAKE_BASE_NAME = 'cmake-%s-%s-%s' % ( |
| PREBUILT_CMAKE_VERSION, CMakePlatformName(), CMakeArch()) |
| |
| |
| def PrebuiltCMakeDir(*args): |
| return GetPrebuilt(PREBUILT_CMAKE_BASE_NAME, *args) |
| |
| |
| def PrebuiltCMakeBin(): |
| if IsMac(): |
| bin_dir = os.path.join('CMake.app', 'Contents', 'bin') |
| else: |
| bin_dir = 'bin' |
| return PrebuiltCMakeDir(bin_dir, 'cmake') |
| |
| |
| def BuilderPlatformName(): |
| return { |
| 'linux': 'linux', |
| 'linux2': 'linux', |
| 'darwin': 'mac', |
| 'win32': 'windows' |
| }[sys.platform] |
| |
| |
| def D8Bin(): |
| if IsMac(): |
| return os.path.join(JSVU_OUT_DIR, 'v8') |
| return Executable(GetInstallDir('bin', 'd8')) |
| |
| |
| # Java installed in the buildbots are too old while emscripten uses closure |
| # compiler that requires Java SE 8.0 (version 52) or above |
| JAVA_VERSION = '9.0.1' |
| |
| |
| def JavaDir(): |
| outdir = GetPrebuilt('jre-' + JAVA_VERSION) |
| if IsMac(): |
| outdir += '.jre' |
| return outdir |
| |
| |
| def JavaBin(): |
| if IsMac(): |
| bin_dir = os.path.join('Contents', 'Home', 'bin') |
| else: |
| bin_dir = 'bin' |
| return Executable(os.path.join(JavaDir(), bin_dir, 'java')) |
| |
| |
| # Known failures. |
| IT_IS_KNOWN = 'known_gcc_test_failures.txt' |
| ASM2WASM_KNOWN_TORTURE_COMPILE_FAILURES = [ |
| os.path.join(SCRIPT_DIR, 'test', 'asm2wasm_compile_' + IT_IS_KNOWN) |
| ] |
| EMWASM_KNOWN_TORTURE_COMPILE_FAILURES = [ |
| os.path.join(SCRIPT_DIR, 'test', 'emwasm_compile_' + IT_IS_KNOWN) |
| ] |
| |
| RUN_KNOWN_TORTURE_FAILURES = [ |
| os.path.join(SCRIPT_DIR, 'test', 'run_' + IT_IS_KNOWN) |
| ] |
| LLD_KNOWN_TORTURE_FAILURES = [ |
| os.path.join(SCRIPT_DIR, 'test', 'lld_' + IT_IS_KNOWN) |
| ] |
| |
| # Exclusions (known failures are compiled and run, and expected to fail, |
| # whereas exclusions are not even run, e.g. because they have UB which |
| # results in infinite loops) |
| LLVM_TORTURE_EXCLUSIONS = [ |
| os.path.join(SCRIPT_DIR, 'test', 'llvm_torture_exclusions') |
| ] |
| |
| RUN_LLVM_TESTSUITE_FAILURES = [ |
| os.path.join(SCRIPT_DIR, 'test', 'llvmtest_known_failures.txt') |
| ] |
| |
| # Optimization levels |
| BARE_TEST_OPT_FLAGS = ['O0', 'O2'] |
| EMSCRIPTEN_TEST_OPT_FLAGS = ['O0', 'O3'] |
| |
| NPROC = multiprocessing.cpu_count() |
| |
| if IsMac(): |
| # Experimental temp fix for crbug.com/829034 stdout write sometimes fails |
| from fcntl import fcntl, F_GETFL, F_SETFL |
| fd = sys.stdout.fileno() |
| flags = fcntl(fd, F_GETFL) |
| fcntl(fd, F_SETFL, flags & ~os.O_NONBLOCK) |
| |
| # Pin the GCC revision so that new torture tests don't break the bot. This |
| # should be manually updated when convenient. |
| GCC_REVISION = 'b6125c702850488ac3bfb1079ae5c9db89989406' |
| GCC_CLONE_DEPTH = 1000 |
| |
| |
| def CopyBinaryToArchive(binary, prefix=''): |
| """All binaries are archived in the same tar file.""" |
| install_bin = GetInstallDir(prefix, 'bin') |
| print('Copying binary %s to archive %s' % (binary, install_bin)) |
| Mkdir(install_bin) |
| shutil.copy2(binary, install_bin) |
| |
| |
| def CopyLibraryToArchive(library, prefix=''): |
| """All libraries are archived in the same tar file.""" |
| install_lib = GetInstallDir(prefix, 'lib') |
| print('Copying library %s to archive %s' % (library, install_lib)) |
| Mkdir(install_lib) |
| shutil.copy2(library, install_lib) |
| |
| |
| def CopyLibraryToSysroot(library): |
| """All libraries are archived in the same tar file.""" |
| install_lib = GetInstallDir('sysroot', 'lib', 'wasm32-wasi') |
| print('Copying library %s to archive %s' % (library, install_lib)) |
| Mkdir(install_lib) |
| shutil.copy2(library, install_lib) |
| |
| |
| def Archive(directory, print_content=False): |
| """Create an archive file from directory.""" |
| # Use the format "native" to the platform |
| if IsWindows(): |
| archive = Zip(directory, print_content) |
| else: |
| archive = Tar(directory, print_content) |
| print('Archive created: %s [%s]' % (archive, os.path.getsize(archive))) |
| return archive |
| |
| |
| def Tar(directory, print_content=False): |
| assert os.path.isdir(directory), 'Must tar a directory to avoid tarbombs' |
| up_directory, basename = os.path.split(directory) |
| tar = os.path.join(up_directory, basename + '.tbz2') |
| Remove(tar) |
| if print_content: |
| proc.check_call( |
| ['find', basename, '-type', 'f', '-exec', 'ls', '-lhS', '{}', '+'], |
| cwd=up_directory) |
| proc.check_call(['tar', 'cjf', tar, basename], cwd=up_directory) |
| proc.check_call(['ls', '-lh', tar], cwd=up_directory) |
| return tar |
| |
| |
| def Zip(directory, print_content=False): |
| assert os.path.isdir(directory), 'Must be a directory' |
| dirname, basename = os.path.split(directory) |
| archive = os.path.join(dirname, basename + '.zip') |
| print('Creating zip archive', archive) |
| with zipfile.ZipFile(archive, 'w', zipfile.ZIP_DEFLATED) as z: |
| for root, dirs, files in os.walk(directory): |
| for name in files: |
| fs_path = os.path.join(root, name) |
| zip_path = os.path.relpath(fs_path, os.path.dirname(directory)) |
| if print_content: |
| print('Adding', fs_path) |
| z.write(fs_path, zip_path) |
| print('Size:', os.stat(archive).st_size) |
| return archive |
| |
| |
| def UploadFile(local_name, remote_name): |
| """Archive the file with the given name, and with the LLVM git hash.""" |
| if not buildbot.IsUploadingBot(): |
| return |
| buildbot.Link( |
| 'download', |
| cloud.Upload( |
| local_name, '%s/%s/%s' % |
| (buildbot.BuilderName(), buildbot.BuildNumber(), remote_name))) |
| |
| |
| def UploadArchive(name, archive): |
| """Archive the tar/zip file with the given name and the build number.""" |
| if not buildbot.IsUploadingBot(): |
| return |
| extension = os.path.splitext(archive)[1] |
| UploadFile(archive, 'wasm-%s%s' % (name, extension)) |
| |
| |
| # Repo and subproject utilities |
| |
| |
| def GitRemoteUrl(cwd, remote): |
| """Get the URL of a remote.""" |
| return proc.check_output( |
| ['git', 'config', '--get', 'remote.%s.url' % remote], |
| cwd=cwd).strip() |
| |
| |
| def RemoteBranch(branch): |
| """Get the remote-qualified branch name to use for waterfall""" |
| return WATERFALL_REMOTE + '/' + branch |
| |
| |
| def GitUpdateRemote(src_dir, git_repo, remote_name): |
| try: |
| proc.check_call(['git', 'remote', 'set-url', remote_name, git_repo], |
| cwd=src_dir) |
| except proc.CalledProcessError: |
| # If proc.check_call fails it throws an exception. 'git remote set-url' |
| # fails when the remote doesn't exist, so we should try to add it. |
| proc.check_call(['git', 'remote', 'add', remote_name, git_repo], |
| cwd=src_dir) |
| |
| |
| class Filter(object): |
| """Filter for source or build rules, to allow including or excluding only |
| selected targets. |
| """ |
| def __init__(self, name=None, include=None, exclude=None): |
| """ |
| include: |
| if present, only items in it will be included (if empty, nothing will |
| be included). |
| exclude: |
| if present, items in it will be excluded. |
| include ane exclude cannot both be present. |
| """ |
| if include and exclude: |
| raise Exception( |
| 'Filter cannot include both include and exclude rules') |
| |
| self.name = name |
| self.include = include |
| self.exclude = exclude |
| |
| def Apply(self, targets): |
| """Return the filtered list of targets.""" |
| all_names = [t.name for t in targets] |
| specified_names = self.include or self.exclude or [] |
| missing_names = [i for i in specified_names if i not in all_names] |
| if missing_names: |
| raise Exception('Invalid step name(s): {0}\n\n' |
| 'Valid {1} steps:\n{2}'.format( |
| missing_names, self.name, |
| TextWrapNameList(prefix='', items=targets))) |
| |
| return [t for t in targets if self.Check(t.name)] |
| |
| def Check(self, target): |
| """Return true if the specified target will be run.""" |
| if self.include is not None: |
| return target in self.include |
| |
| if self.exclude is not None: |
| return target not in self.exclude |
| return True |
| |
| def All(self): |
| """Return true if all possible targets will be run.""" |
| return self.include is None and not self.exclude |
| |
| def Any(self): |
| """Return true if any targets can be run.""" |
| return self.include is None or len(self.include) |
| |
| |
| class Source(object): |
| """Metadata about a sync-able source repo on the waterfall""" |
| def __init__(self, name, src_dir, git_repo, |
| checkout=RemoteBranch('master'), depth=None, |
| custom_sync=None, os_filter=None): |
| self.name = name |
| self.src_dir = src_dir |
| self.git_repo = git_repo |
| self.checkout = checkout |
| self.depth = depth |
| self.custom_sync = custom_sync |
| self.os_filter = os_filter |
| |
| # Ensure that git URLs end in .git. We have had issues in the past |
| # where github would not recognize the requests correctly otherwise due |
| # to chromium's builders setting custom GIT_USER_AGENT: |
| # https://bugs.chromium.org/p/chromium/issues/detail?id=711775 |
| if git_repo: |
| assert git_repo.endswith('.git'), 'Git URLs should end in .git' |
| |
| def Sync(self, good_hashes=None): |
| if self.os_filter and not self.os_filter.Check(BuilderPlatformName()): |
| print("Skipping %s: Doesn't work on %s" % |
| (self.name, BuilderPlatformName())) |
| return |
| if good_hashes and good_hashes.get(self.name): |
| self.checkout = good_hashes[self.name] |
| if self.custom_sync: |
| self.custom_sync(self.name, self.src_dir, self.git_repo) |
| else: |
| self.GitCloneFetchCheckout() |
| |
| def GitCloneFetchCheckout(self): |
| """Clone a git repo if not already cloned, then fetch and checkout.""" |
| if os.path.isdir(self.src_dir): |
| print('%s directory already exists' % self.name) |
| else: |
| clone = ['clone', self.git_repo, self.src_dir] |
| if self.depth: |
| clone.append('--depth') |
| clone.append(str(self.depth)) |
| proc.check_call(['git'] + clone) |
| |
| GitUpdateRemote(self.src_dir, self.git_repo, WATERFALL_REMOTE) |
| proc.check_call(['git', 'fetch', '--force', '--prune', '--tags', |
| WATERFALL_REMOTE], |
| cwd=self.src_dir) |
| if not self.checkout.startswith(WATERFALL_REMOTE + '/'): |
| sys.stderr.write( |
| ('WARNING: `git checkout %s` not based on waterfall ' |
| 'remote (%s), checking out local branch' % |
| (self.checkout, WATERFALL_REMOTE))) |
| proc.check_call(['git', 'checkout', self.checkout], cwd=self.src_dir) |
| proc.check_call(['git', 'submodule', 'update', '--init'], |
| cwd=self.src_dir) |
| |
| def CurrentGitInfo(self): |
| if not os.path.exists(self.src_dir): |
| return None |
| |
| def pretty(fmt): |
| return proc.check_output( |
| ['git', 'log', '-n1', |
| '--pretty=format:%s' % fmt], |
| cwd=self.src_dir).strip() |
| |
| try: |
| remote = GitRemoteUrl(self.src_dir, WATERFALL_REMOTE) |
| except proc.CalledProcessError: |
| # Not all checkouts have the '_waterfall' remote (e.g. the |
| # waterfall itself) so fall back to origin on failure |
| remote = GitRemoteUrl(self.src_dir, 'origin') |
| |
| return { |
| 'hash': pretty('%H'), |
| 'name': pretty('%aN'), |
| 'email': pretty('%ae'), |
| 'subject': pretty('%s'), |
| 'remote': remote, |
| } |
| |
| def PrintGitStatus(self): |
| """"Print the current git status for the sync target.""" |
| print('<<<<<<<<<< STATUS FOR', self.name, '>>>>>>>>>>') |
| if os.path.exists(self.src_dir): |
| proc.check_call(['git', 'status'], cwd=self.src_dir) |
| print() |
| |
| |
| def ChromiumFetchSync(name, work_dir, git_repo, |
| checkout=RemoteBranch('master')): |
| """Some Chromium projects want to use gclient for clone and |
| dependencies.""" |
| if os.path.isdir(work_dir): |
| print('%s directory already exists' % name) |
| else: |
| # Create Chromium repositories one deeper, separating .gclient files. |
| parent = os.path.split(work_dir)[0] |
| Mkdir(parent) |
| proc.check_call(['gclient', 'config', git_repo], cwd=parent) |
| proc.check_call(['git', 'clone', git_repo], cwd=parent) |
| |
| GitUpdateRemote(work_dir, git_repo, WATERFALL_REMOTE) |
| proc.check_call(['git', 'fetch', WATERFALL_REMOTE], cwd=work_dir) |
| proc.check_call(['git', 'checkout', checkout], cwd=work_dir) |
| proc.check_call(['gclient', 'sync'], cwd=work_dir) |
| return (name, work_dir) |
| |
| |
| def SyncToolchain(name, src_dir, git_repo): |
| if IsWindows(): |
| host_toolchains.SyncWinToolchain() |
| else: |
| host_toolchains.SyncPrebuiltClang(src_dir) |
| cc = GetPrebuiltClang('clang') |
| cxx = GetPrebuiltClang('clang++') |
| assert os.path.isfile(cc), 'Expect clang at %s' % cc |
| assert os.path.isfile(cxx), 'Expect clang++ at %s' % cxx |
| |
| |
| def SyncArchive(out_dir, name, url, create_out_dir=False): |
| """Download and extract an archive (zip, tar.gz or tar.xz) file from a URL. |
| |
| The extraction happens in the prebuilt dir. If create_out_dir is True, |
| out_dir will be created and the archive will be extracted inside. Otherwise |
| the archive is expected to contain a top-level directory with all the |
| files; this is expected to be 'out_dir', so if 'out_dir' already exists |
| then the download will be skipped. |
| """ |
| stamp_file = os.path.join(out_dir, 'stamp.txt') |
| if os.path.isdir(out_dir): |
| if os.path.isfile(stamp_file): |
| with open(stamp_file) as f: |
| stamp_url = f.read().strip() |
| if stamp_url == url: |
| print('%s directory already exists' % name) |
| return |
| print('%s directory exists but is not up-to-date' % name) |
| print('Downloading %s from %s' % (name, url)) |
| |
| if create_out_dir: |
| os.makedirs(out_dir) |
| work_dir = out_dir |
| else: |
| work_dir = os.path.dirname(out_dir) |
| |
| try: |
| f = urlopen(url) |
| print('URL: %s' % f.geturl()) |
| print('Info: %s' % f.info()) |
| with tempfile.NamedTemporaryFile() as t: |
| t.write(f.read()) |
| t.flush() |
| t.seek(0) |
| print('Extracting into %s' % work_dir) |
| ext = os.path.splitext(url)[-1] |
| if ext == '.zip': |
| with zipfile.ZipFile(t, 'r') as zip: |
| zip.extractall(path=work_dir) |
| elif ext == '.xz': |
| proc.check_call(['tar', '-xf', t.name], cwd=work_dir) |
| else: |
| tarfile.open(fileobj=t).extractall(path=work_dir) |
| except URLError as e: |
| print('Error downloading %s: %s' % (url, e)) |
| raise |
| |
| with open(stamp_file, 'w') as f: |
| f.write(url + '\n') |
| |
| |
| def SyncPrebuiltCMake(name, src_dir, git_repo): |
| extension = '.zip' if IsWindows() else '.tar.gz' |
| url = WASM_STORAGE_BASE + PREBUILT_CMAKE_BASE_NAME + extension |
| SyncArchive(PrebuiltCMakeDir(), 'cmake', url) |
| |
| |
| def SyncPrebuiltNodeJS(name, src_dir, git_repo): |
| extension = { |
| 'darwin': 'tar.xz', |
| 'linux': 'tar.xz', |
| 'win32': 'zip' |
| }[sys.platform] |
| out_dir = GetPrebuilt(NODE_BASE_NAME + NodePlatformName()) |
| tarball = NODE_BASE_NAME + NodePlatformName() + '.' + extension |
| node_url = WASM_STORAGE_BASE + tarball |
| return SyncArchive(out_dir, name, node_url) |
| |
| |
| # Utilities needed for running LLVM regression tests on Windows |
| def SyncGNUWin32(name, src_dir, git_repo): |
| if not IsWindows(): |
| return |
| url = WASM_STORAGE_BASE + GNUWIN32_ZIP |
| return SyncArchive(GetPrebuilt('gnuwin32'), name, url) |
| |
| |
| def SyncPrebuiltJava(name, src_dir, git_repo): |
| platform = { |
| 'linux': 'linux', |
| 'linux2': 'linux', |
| 'darwin': 'osx', |
| 'win32': 'windows' |
| }[sys.platform] |
| tarball = 'jre-' + JAVA_VERSION + '_' + platform + '-x64_bin.tar.gz' |
| java_url = WASM_STORAGE_BASE + tarball |
| SyncArchive(JavaDir(), name, java_url) |
| |
| |
| def SyncLinuxSysroot(name, src_dir, git_repo): |
| if not (IsLinux() and host_toolchains.ShouldUseSysroot()): |
| return |
| SyncArchive(GetPrebuilt(LINUX_SYSROOT), |
| name, |
| LINUX_SYSROOT_URL, |
| create_out_dir=True) |
| |
| |
| def NoSync(*args): |
| pass |
| |
| |
| def AllSources(): |
| return [ |
| Source('waterfall', SCRIPT_DIR, None, custom_sync=NoSync), |
| Source('llvm', GetSrcDir('llvm-project'), |
| LLVM_GIT_BASE + 'llvm-project.git', |
| checkout=RemoteBranch('main')), |
| Source('llvm-test-suite', GetSrcDir('llvm-test-suite'), |
| LLVM_GIT_BASE + 'llvm-test-suite.git'), |
| Source('emscripten', GetSrcDir('emscripten'), |
| EMSCRIPTEN_GIT_BASE + 'emscripten.git'), |
| Source('gcc', GetSrcDir('gcc'), |
| GIT_MIRROR_BASE + 'chromiumos/third_party/gcc.git', |
| checkout=GCC_REVISION, depth=GCC_CLONE_DEPTH), |
| Source('v8', work_dirs.GetV8(), GIT_MIRROR_BASE + 'v8/v8.git', |
| custom_sync=ChromiumFetchSync), |
| Source('host-toolchain', work_dirs.GetV8(), '', |
| custom_sync=SyncToolchain), |
| Source('cmake', '', '', # The source and git args are ignored. |
| custom_sync=SyncPrebuiltCMake), |
| Source('nodejs', '', '', # The source and git args are ignored. |
| custom_sync=SyncPrebuiltNodeJS), |
| Source('gnuwin32', '', '', # The source and git args are ignored. |
| custom_sync=SyncGNUWin32), |
| Source('wabt', GetSrcDir('wabt'), WASM_GIT_BASE + 'wabt.git'), |
| Source('binaryen', GetSrcDir('binaryen'), |
| WASM_GIT_BASE + 'binaryen.git'), |
| Source('wasi-libc', GetSrcDir('wasi-libc'), |
| 'https://github.com/CraneStation/wasi-libc.git'), |
| Source('java', '', '', # The source and git args are ignored. |
| custom_sync=SyncPrebuiltJava), |
| Source('sysroot', '', '', # The source and git args are ignored. |
| custom_sync=SyncLinuxSysroot) |
| ] |
| |
| |
| def Clobber(): |
| # Don't automatically clobber non-bot (local) work directories |
| if not buildbot.IsBot() and not options.clobber: |
| return |
| |
| clobber = options.clobber or buildbot.ShouldClobber() |
| clobber_file = GetBuildDir('clobber_version.txt') |
| if not clobber: |
| if not os.path.exists(clobber_file): |
| print('Clobber file %s does not exist.' % clobber_file) |
| clobber = True |
| else: |
| existing_tag = int(open(clobber_file).read().strip()) |
| if existing_tag != CLOBBER_BUILD_TAG: |
| print('Clobber file %s has tag %s.' % |
| (clobber_file, existing_tag)) |
| clobber = True |
| |
| if not clobber: |
| return |
| |
| buildbot.Step('Clobbering work dir') |
| if buildbot.IsEmscriptenReleasesBot() or not buildbot.IsBot(): |
| # Never clear source dirs locally. |
| # On emscripten-releases, depot_tools and the recipe clear the rest. |
| dirs = [work_dirs.GetBuild()] |
| else: |
| dirs = work_dirs.GetAll() |
| for work_dir in dirs: |
| Remove(work_dir) |
| Mkdir(work_dir) |
| # Also clobber v8 |
| v8_dir = os.path.join(work_dirs.GetV8(), V8_BUILD_SUBDIR) |
| Remove(v8_dir) |
| with open(clobber_file, 'w') as f: |
| f.write('%s\n' % CLOBBER_BUILD_TAG) |
| |
| |
| def SyncRepos(filter, sync_lkgr=False): |
| if not filter.Any(): |
| return |
| buildbot.Step('Sync Repos') |
| |
| good_hashes = None |
| if sync_lkgr: |
| lkgr_file = GetBuildDir('lkgr.json') |
| cloud.Download('%s/lkgr.json' % BuilderPlatformName(), lkgr_file) |
| lkgr = json.loads(open(lkgr_file).read()) |
| good_hashes = {} |
| for k, v in lkgr['repositories'].iteritems(): |
| good_hashes[k] = v.get('hash') if v else None |
| |
| for repo in filter.Apply(AllSources()): |
| repo.Sync(good_hashes) |
| |
| |
| def GetRepoInfo(): |
| """Collect a readable form of all repo information here, preventing the |
| summary from getting out of sync with the actual list of repos.""" |
| info = {} |
| for r in AllSources(): |
| info[r.name] = r.CurrentGitInfo() |
| return info |
| |
| |
| # Build rules |
| |
| def OverrideCMakeCompiler(): |
| if not host_toolchains.ShouldForceHostClang(): |
| return [] |
| cc = 'clang-cl' if IsWindows() else 'clang' |
| cxx = 'clang-cl' if IsWindows() else 'clang++' |
| tools = [ |
| '-DCMAKE_C_COMPILER=' + Executable(GetPrebuiltClang(cc)), |
| '-DCMAKE_CXX_COMPILER=' + Executable(GetPrebuiltClang(cxx)), |
| ] |
| if IsWindows(): |
| tools.append('-DCMAKE_LINKER=' + |
| Executable(GetPrebuiltClang('lld-link'))) |
| |
| return tools |
| |
| |
| def CMakeCommandBase(): |
| command = [PrebuiltCMakeBin(), '-G', 'Ninja'] |
| # Python's location could change, so always update CMake's cache |
| command.append('-DPYTHON_EXECUTABLE=%s' % sys.executable) |
| command.append('-DCMAKE_EXPORT_COMPILE_COMMANDS=ON') |
| command.append('-DCMAKE_BUILD_TYPE=Release') |
| if IsMac(): |
| # Target MacOS Sierra (10.12) |
| command.append('-DCMAKE_OSX_DEPLOYMENT_TARGET=10.12') |
| elif IsWindows(): |
| # CMake's usual logic fails to find LUCI's git on Windows |
| git_exe = proc.Which('git') |
| command.append('-DGIT_EXECUTABLE=%s' % git_exe) |
| return command |
| |
| |
| def CMakeCommandNative(args, build_dir): |
| command = CMakeCommandBase() |
| command.append('-DCMAKE_INSTALL_PREFIX=%s' % GetInstallDir()) |
| if IsLinux() and host_toolchains.ShouldUseSysroot(): |
| command.append('-DCMAKE_SYSROOT=%s' % GetPrebuilt(LINUX_SYSROOT)) |
| command.append('-DCMAKE_EXE_LINKER_FLAGS=-static-libstdc++') |
| command.append('-DCMAKE_SHARED_LINKER_FLAGS=-static-libstdc++') |
| |
| elif IsMac() and host_toolchains.ShouldUseSysroot(): |
| # Get XCode SDK path. |
| xcode_sdk_path = proc.check_output(['xcrun', |
| '--show-sdk-path']).strip() |
| # Create relpath symlink if it doesn't exist. |
| # If it does exist, but points to a different location, update it. |
| symlink_path = os.path.join(build_dir, 'xcode_sdk') |
| if os.path.lexists( |
| symlink_path) and os.readlink(symlink_path) != xcode_sdk_path: |
| os.remove(symlink_path) |
| if not os.path.exists(symlink_path): |
| os.symlink(xcode_sdk_path, symlink_path) |
| command.append('-DCMAKE_OSX_SYSROOT=%s' % symlink_path) |
| command.append('-DCMAKE_SYSROOT=%s' % symlink_path) |
| |
| if host_toolchains.ShouldForceHostClang(): |
| command.extend(OverrideCMakeCompiler()) |
| # Goma doesn't have MSVC in its cache, so don't use it in this case |
| command.extend(host_toolchains.CMakeLauncherFlags()) |
| command.extend(args) |
| # On Windows, CMake chokes on paths containing backslashes that come from |
| # the command line. Probably they just need to be escaped, but using '/' |
| # instead is easier and works just as well. |
| return [arg.replace('\\', '/') for arg in command] |
| |
| |
| def CMakeCommandWasi(args): |
| command = CMakeCommandBase() |
| command.append('-DCMAKE_TOOLCHAIN_FILE=%s' % |
| GetInstallDir(CMAKE_TOOLCHAIN_FILE)) |
| command.extend(args) |
| return command |
| |
| |
| def CopyLLVMTools(build_dir, prefix=''): |
| # The following aren't useful for now, and take up space. |
| # DLLs are in bin/ on Windows but in lib/ on posix. |
| for unneeded_tool in ('clang-check', 'clang-cl', 'clang-cpp', |
| 'clang-extdef-mapping', 'clang-format', |
| 'clang-func-mapping', 'clang-import-test', |
| 'clang-offload-bundler', 'clang-refactor', |
| 'clang-rename', 'clang-scan-deps', 'libclang.dll', |
| 'lld-link', 'ld.lld', 'lld64.lld', 'llvm-lib'): |
| Remove(GetInstallDir(prefix, 'bin', Executable(unneeded_tool))) |
| |
| for lib in ['libclang.%s' for suffix in ('so.*', 'dylib')]: |
| Remove(GetInstallDir(prefix, 'lib', lib)) |
| |
| # The following are useful, LLVM_INSTALL_TOOLCHAIN_ONLY did away with them. |
| extra_bins = map(Executable, [ |
| 'FileCheck', 'llc', 'llvm-as', 'llvm-dis', 'llvm-link', 'llvm-mc', |
| 'llvm-nm', 'llvm-objdump', 'llvm-readobj', 'llvm-size', 'opt', |
| 'llvm-dwarfdump' |
| ]) |
| for p in [ |
| glob.glob(os.path.join(build_dir, 'bin', b)) for b in extra_bins |
| ]: |
| for e in p: |
| CopyBinaryToArchive(os.path.join(build_dir, 'bin', e), prefix) |
| |
| |
| def BuildEnv(build_dir, use_gnuwin32=False, bin_subdir=False, |
| runtime='Release'): |
| if not IsWindows(): |
| return None |
| cc_env = host_toolchains.SetUpVSEnv(build_dir) |
| if use_gnuwin32: |
| cc_env['PATH'] = cc_env['PATH'] + os.pathsep + GetSrcDir( |
| 'gnuwin32', 'bin') |
| bin_dir = build_dir if not bin_subdir else os.path.join(build_dir, 'bin') |
| Mkdir(bin_dir) |
| assert runtime in ['Release', 'Debug'] |
| host_toolchains.CopyDlls(bin_dir, runtime) |
| return cc_env |
| |
| |
| def LLVM(): |
| buildbot.Step('LLVM') |
| build_dir = os.path.join(work_dirs.GetBuild(), 'llvm-out') |
| Mkdir(build_dir) |
| cc_env = BuildEnv(build_dir, bin_subdir=True) |
| build_dylib = 'ON' if not IsWindows() and not options.use_lto else 'OFF' |
| command = CMakeCommandNative([ |
| GetLLVMSrcDir('llvm'), |
| '-DCMAKE_CXX_FLAGS=-Wno-nonportable-include-path', |
| '-DLLVM_ENABLE_LIBXML2=OFF', |
| '-DLLVM_INCLUDE_EXAMPLES=OFF', |
| '-DCOMPILER_RT_BUILD_XRAY=OFF', |
| '-DCOMPILER_RT_INCLUDE_TESTS=OFF', |
| '-DCOMPILER_RT_ENABLE_IOS=OFF', |
| '-DLLVM_BUILD_LLVM_DYLIB=%s' % build_dylib, |
| '-DLLVM_LINK_LLVM_DYLIB=%s' % build_dylib, |
| '-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON', |
| # Our mac bot's toolchain's ld64 is too old for trunk libLTO. |
| '-DLLVM_TOOL_LTO_BUILD=OFF', |
| '-DLLVM_INSTALL_TOOLCHAIN_ONLY=ON', |
| '-DLLVM_TARGETS_TO_BUILD=X86;WebAssembly', |
| '-DLLVM_ENABLE_PROJECTS=lld;clang', |
| # linking libtinfo dynamically causes problems on some linuxes, |
| # https://github.com/emscripten-core/emsdk/issues/252 |
| '-DLLVM_ENABLE_TERMINFO=%d' % (not IsLinux()), |
| ], build_dir) |
| |
| if options.use_lto: |
| command.extend(['-DLLVM_ENABLE_ASSERTIONS=OFF', |
| '-DLLVM_BUILD_TESTS=OFF', |
| '-DLLVM_INCLUDE_TESTS=OFF', |
| '-DLLVM_ENABLE_LTO=Thin', |
| '-DLLVM_ENABLE_LLD=ON']) |
| else: |
| command.extend(['-DLLVM_ENABLE_ASSERTIONS=ON']) |
| |
| jobs = host_toolchains.NinjaJobs() |
| |
| proc.check_call(command, cwd=build_dir, env=cc_env) |
| proc.check_call(['ninja', '-v'] + jobs, cwd=build_dir, env=cc_env) |
| proc.check_call(['ninja', 'install'] + jobs, cwd=build_dir, env=cc_env) |
| CopyLLVMTools(build_dir) |
| install_bin = GetInstallDir('bin') |
| for target in ('clang', 'clang++'): |
| for link in 'wasm32-', 'wasm32-wasi-': |
| link = os.path.join(install_bin, link + target) |
| if not IsWindows(): |
| if not os.path.islink(Executable(link)): |
| os.symlink(Executable(target), Executable(link)) |
| else: |
| # Windows has no symlinks (at least not from python). Also |
| # clang won't work as a native compiler anyway, so just install |
| # it as wasm32-wasi-clang |
| shutil.copy2(Executable(os.path.join(install_bin, target)), |
| Executable(link)) |
| |
| |
| def LLVMTestDepends(): |
| buildbot.Step('LLVM Test Dependencies') |
| build_dir = os.path.join(work_dirs.GetBuild(), 'llvm-out') |
| proc.check_call(['ninja', '-v', 'test-depends'] + |
| host_toolchains.NinjaJobs(), |
| cwd=build_dir, |
| env=BuildEnv(build_dir, bin_subdir=True)) |
| |
| |
| def TestLLVMRegression(): |
| build_dir = os.path.join(work_dirs.GetBuild(), 'llvm-out') |
| cc_env = BuildEnv(build_dir, bin_subdir=True) |
| if not os.path.isdir(build_dir): |
| print('LLVM Build dir %s does not exist' % build_dir) |
| buildbot.Fail() |
| return |
| |
| def RunWithUnixUtils(cmd, **kwargs): |
| if IsWindows(): |
| return proc.check_call(['git', 'bash'] + cmd, **kwargs) |
| else: |
| return proc.check_call(cmd, **kwargs) |
| |
| try: |
| buildbot.Step('LLVM regression tests') |
| RunWithUnixUtils(['ninja', 'check-all'], cwd=build_dir, env=cc_env) |
| except proc.CalledProcessError: |
| buildbot.FailUnless(lambda: IsWindows()) |
| |
| |
| def V8(): |
| buildbot.Step('V8') |
| src_dir = work_dirs.GetV8() |
| out_dir = os.path.join(src_dir, V8_BUILD_SUBDIR) |
| vpython = 'vpython' + ('.bat' if IsWindows() else '') |
| |
| # Generate and write a GN args file. |
| gn_args = 'is_debug = false\ntarget_cpu = "x64"\n' |
| if host_toolchains.UsingGoma(): |
| gn_args += 'use_goma = true\n' |
| gn_args += 'goma_dir = "%s"\n' % host_toolchains.GomaDir() |
| Mkdir(out_dir) |
| with open(os.path.join(out_dir, 'args.gn'), 'w') as f: |
| f.write(gn_args) |
| # Invoke GN to generate. We need to use vpython as the script interpreter |
| # since GN's scripts seem to require python2. Hence we need to invoke GN |
| # directly rather than using one of V8's GN wrapper scripts (e.g. mb.py). |
| # But because V8 has a different directory layout from Chrome, we can't |
| # just use the GN wrapper in depot_tools, we have to invoke the one in the |
| # V8 buildtools dir directly. |
| gn_platform = 'linux64' if IsLinux() else 'mac' if IsMac() else 'win' |
| gn_exe = Executable(os.path.join(src_dir, 'buildtools', gn_platform, 'gn')) |
| proc.check_call([gn_exe, 'gen', out_dir, '--script-executable=' + vpython], |
| cwd=src_dir) |
| |
| jobs = host_toolchains.NinjaJobs() |
| proc.check_call(['ninja', '-v', '-C', out_dir, 'd8', 'unittests'] + jobs, |
| cwd=src_dir) |
| # Copy the V8 snapshot as well as the ICU data file for timezone data. |
| # icudtl.dat is the little-endian version, which goes with x64. |
| to_archive = [Executable('d8'), 'snapshot_blob.bin', 'icudtl.dat'] |
| for a in to_archive: |
| CopyBinaryToArchive(os.path.join(out_dir, a)) |
| |
| |
| def Jsvu(): |
| buildbot.Step('jsvu') |
| jsvu_dir = os.path.join(work_dirs.GetBuild(), 'jsvu') |
| Mkdir(jsvu_dir) |
| |
| if IsWindows(): |
| # jsvu OS identifiers: |
| # https://github.com/GoogleChromeLabs/jsvu#supported-engines |
| os_id = 'windows64' |
| js_engines = 'chakra' |
| elif IsMac(): |
| os_id = 'mac64' |
| js_engines = 'javascriptcore,v8' |
| else: |
| os_id = 'linux64' |
| js_engines = 'javascriptcore' |
| |
| try: |
| # https://github.com/GoogleChromeLabs/jsvu#installation |
| # ...except we install it locally instead of globally. |
| proc.check_call(['npm', 'install', 'jsvu'], cwd=jsvu_dir) |
| |
| jsvu_bin = Executable( |
| os.path.join(jsvu_dir, 'node_modules', 'jsvu', 'cli.js')) |
| # https://github.com/GoogleChromeLabs/jsvu#integration-with-non-interactive-environments |
| proc.check_call( |
| [jsvu_bin, |
| '--os=%s' % os_id, |
| '--engines=%s' % js_engines]) |
| |
| # $HOME/.jsvu/chakra is now available on Windows. |
| # $HOME/.jsvu/javascriptcore is now available on Mac. |
| |
| # TODO: Install the JSC binary in the output package, and add the |
| # version info to the repo info JSON file (currently in GetRepoInfo) |
| except proc.CalledProcessError: |
| buildbot.Warn() |
| |
| |
| def Wabt(): |
| buildbot.Step('WABT') |
| out_dir = os.path.join(work_dirs.GetBuild(), 'wabt-out') |
| Mkdir(out_dir) |
| cc_env = BuildEnv(out_dir) |
| |
| cmd = CMakeCommandNative([GetSrcDir('wabt'), |
| '-DBUILD_TESTS=OFF', '-DBUILD_LIBWASM=OFF'], |
| out_dir) |
| proc.check_call(cmd, cwd=out_dir, env=cc_env) |
| |
| proc.check_call(['ninja', '-v'] + host_toolchains.NinjaJobs(), |
| cwd=out_dir, |
| env=cc_env) |
| proc.check_call(['ninja', 'install'], cwd=out_dir, env=cc_env) |
| |
| |
| def Binaryen(): |
| buildbot.Step('binaryen') |
| out_dir = os.path.join(work_dirs.GetBuild(), 'binaryen-out') |
| Mkdir(out_dir) |
| # Currently it's a bad idea to do a non-asserts build of Binaryen |
| cc_env = BuildEnv(out_dir, bin_subdir=True, runtime='Debug') |
| |
| cmake_command = CMakeCommandNative([GetSrcDir('binaryen')], out_dir) |
| if options.use_lto: |
| cmake_command.append('-DBYN_ENABLE_LTO=ON') |
| proc.check_call(cmake_command, |
| cwd=out_dir, |
| env=cc_env) |
| proc.check_call(['ninja', '-v'] + host_toolchains.NinjaJobs(), |
| cwd=out_dir, |
| env=cc_env) |
| proc.check_call(['ninja', 'install'], cwd=out_dir, env=cc_env) |
| |
| |
| def InstallEmscripten(): |
| src_dir = GetSrcDir('emscripten') |
| em_install_dir = GetInstallDir('emscripten') |
| Remove(em_install_dir) |
| print('Installing emscripten into %s' % em_install_dir) |
| proc.check_call([os.path.join('tools', 'install.py'), em_install_dir], |
| cwd=src_dir) |
| |
| print('Running npm install ...') |
| proc.check_call(['npm', 'ci', '--no-optional'], cwd=em_install_dir) |
| |
| # Manually install the appropriate native Closure Compiler package |
| # This is currently needed because npm ci will install the packages |
| # for Closure for all platforms, adding 180MB to the download size |
| # There are two problems here: |
| # 1. npm ci does not consider the platform of optional dependencies |
| # https://github.com/npm/cli/issues/558 |
| # 2. A bug with the native compiler has bloated the packages from |
| # 30MB to almost 300MB |
| # https://github.com/google/closure-compiler-npm/issues/186 |
| # If either of these bugs are fixed we could consider removing this |
| # hack. |
| native = None |
| if IsMac(): |
| native = 'google-closure-compiler-osx' |
| elif IsWindows(): |
| native = 'google-closure-compiler-windows' |
| elif IsLinux(): |
| native = 'google-closure-compiler-linux' |
| proc.check_call(['npm', 'install', native], cwd=em_install_dir) |
| |
| |
| def Emscripten(): |
| InstallEmscripten() |
| |
| def WriteEmscriptenConfig(infile, outfile): |
| with open(infile) as config: |
| text = config.read().replace('{{WASM_INSTALL}}', |
| WindowsFSEscape(GetInstallDir())) |
| text = text.replace('{{PREBUILT_NODE}}', |
| WindowsFSEscape(NodeBin())) |
| text = text.replace('{{PREBUILT_JAVA}}', |
| WindowsFSEscape(JavaBin())) |
| with open(outfile, 'w') as config: |
| config.write(text) |
| |
| # Set up the emscripten config and compile the libraries |
| buildbot.Step('emscripten') |
| config = GetInstallDir(EMSCRIPTEN_CONFIG_UPSTREAM) |
| print('Config file: ', config) |
| src_config = os.path.join(SCRIPT_DIR, os.path.basename(config)) |
| WriteEmscriptenConfig(src_config, config) |
| |
| env = os.environ.copy() |
| env['EM_CONFIG'] = config |
| try: |
| # Use emscripten's embuilder to prebuild the system libraries. |
| # This depends on binaryen already being built and installed into the |
| # archive/install dir. |
| proc.check_call([ |
| sys.executable, |
| os.path.join(GetInstallDir('emscripten'), 'embuilder.py'), 'build', |
| 'SYSTEM' |
| ], env=env) |
| |
| except proc.CalledProcessError: |
| # Note the failure but allow the build to continue. |
| buildbot.Fail() |
| |
| # Remove the sanity file. This means it will get generated on first |
| # use without clearing the cache. |
| sanity = GetInstallDir('emscripten', 'cache', 'sanity.txt') |
| if os.path.exists(sanity): |
| os.remove(sanity) |
| |
| |
| def CompilerRT(): |
| # TODO(sbc): Figure out how to do this step as part of the llvm build. |
| # I suspect that this can be done using the llvm/runtimes directory but |
| # have yet to make it actually work this way. |
| buildbot.Step('compiler-rt') |
| |
| build_dir = os.path.join(work_dirs.GetBuild(), 'compiler-rt-out') |
| # TODO(sbc): Remove this. |
| # The compiler-rt doesn't currently rebuild libraries when a new -DCMAKE_AR |
| # value is specified. |
| if os.path.isdir(build_dir): |
| Remove(build_dir) |
| |
| Mkdir(build_dir) |
| src_dir = GetLLVMSrcDir('compiler-rt') |
| cc_env = BuildEnv(src_dir, bin_subdir=True) |
| command = CMakeCommandWasi([ |
| os.path.join(src_dir, 'lib', 'builtins'), |
| '-DCMAKE_C_COMPILER_WORKS=ON', '-DCOMPILER_RT_BAREMETAL_BUILD=On', |
| '-DCOMPILER_RT_BUILD_XRAY=OFF', '-DCOMPILER_RT_INCLUDE_TESTS=OFF', |
| '-DCOMPILER_RT_ENABLE_IOS=OFF', '-DCOMPILER_RT_DEFAULT_TARGET_ONLY=On', |
| '-DLLVM_CONFIG_PATH=' + Executable( |
| os.path.join(work_dirs.GetBuild(), 'llvm-out', 'bin', |
| 'llvm-config')), |
| '-DCMAKE_INSTALL_PREFIX=' + GetInstallDir('lib', 'clang', LLVM_VERSION) |
| ]) |
| |
| proc.check_call(command, cwd=build_dir, env=cc_env) |
| proc.check_call(['ninja', '-v'], cwd=build_dir, env=cc_env) |
| proc.check_call(['ninja', 'install'], cwd=build_dir, env=cc_env) |
| |
| |
| def LibCXX(): |
| buildbot.Step('libcxx') |
| build_dir = os.path.join(work_dirs.GetBuild(), 'libcxx-out') |
| if os.path.isdir(build_dir): |
| Remove(build_dir) |
| Mkdir(build_dir) |
| src_dir = GetLLVMSrcDir('libcxx') |
| cc_env = BuildEnv(src_dir, bin_subdir=True) |
| command = CMakeCommandWasi([ |
| src_dir, |
| '-DCMAKE_EXE_LINKER_FLAGS=-nostdlib++', |
| '-DLIBCXX_ENABLE_THREADS=OFF', |
| '-DLIBCXX_ENABLE_SHARED=OFF', |
| '-DLIBCXX_ENABLE_FILESYSTEM=OFF', |
| '-DLIBCXX_HAS_MUSL_LIBC=ON', |
| '-DLIBCXX_CXX_ABI=libcxxabi', |
| '-DLIBCXX_LIBDIR_SUFFIX=/wasm32-wasi', |
| '-DLIBCXX_CXX_ABI_INCLUDE_PATHS=' + |
| GetLLVMSrcDir('libcxxabi', 'include'), |
| '-DLLVM_PATH=' + GetLLVMSrcDir('llvm'), |
| ]) |
| |
| proc.check_call(command, cwd=build_dir, env=cc_env) |
| proc.check_call(['ninja', '-v'], cwd=build_dir, env=cc_env) |
| proc.check_call(['ninja', 'install'], cwd=build_dir, env=cc_env) |
| |
| |
| def LibCXXABI(): |
| buildbot.Step('libcxxabi') |
| build_dir = os.path.join(work_dirs.GetBuild(), 'libcxxabi-out') |
| if os.path.isdir(build_dir): |
| Remove(build_dir) |
| Mkdir(build_dir) |
| src_dir = GetLLVMSrcDir('libcxxabi') |
| cc_env = BuildEnv(src_dir, bin_subdir=True) |
| command = CMakeCommandWasi([ |
| src_dir, |
| '-DCMAKE_EXE_LINKER_FLAGS=-nostdlib++', |
| '-DLIBCXXABI_ENABLE_PIC=OFF', |
| '-DLIBCXXABI_ENABLE_SHARED=OFF', |
| '-DLIBCXXABI_ENABLE_THREADS=OFF', |
| '-DLIBCXXABI_LIBDIR_SUFFIX=/wasm32-wasi', |
| '-DLIBCXXABI_LIBCXX_PATH=' + GetLLVMSrcDir('libcxx'), |
| '-DLIBCXXABI_LIBCXX_INCLUDES=' + |
| GetInstallDir('sysroot', 'include', 'c++', 'v1'), |
| '-DLLVM_PATH=' + GetLLVMSrcDir('llvm'), |
| ]) |
| |
| proc.check_call(command, cwd=build_dir, env=cc_env) |
| proc.check_call(['ninja', '-v'], cwd=build_dir, env=cc_env) |
| proc.check_call(['ninja', 'install'], cwd=build_dir, env=cc_env) |
| CopyLibraryToSysroot(os.path.join(SCRIPT_DIR, 'libc++abi.imports')) |
| |
| |
| def WasiLibc(): |
| buildbot.Step('Wasi') |
| build_dir = os.path.join(work_dirs.GetBuild(), 'wasi-libc-out') |
| if os.path.isdir(build_dir): |
| Remove(build_dir) |
| cc_env = BuildEnv(build_dir, use_gnuwin32=True) |
| src_dir = GetSrcDir('wasi-libc') |
| cmd = [ |
| proc.Which('make'), |
| '-j%s' % NPROC, 'SYSROOT=' + build_dir, |
| 'WASM_CC=' + GetInstallDir('bin', 'clang') |
| ] |
| proc.check_call(cmd, env=cc_env, cwd=src_dir) |
| CopyTree(build_dir, GetInstallDir('sysroot')) |
| |
| # We add the cmake toolchain file and out JS polyfill script to make using |
| # the wasi toolchain easier. |
| shutil.copy2(os.path.join(SCRIPT_DIR, CMAKE_TOOLCHAIN_FILE), |
| GetInstallDir(CMAKE_TOOLCHAIN_FILE)) |
| Remove(GetInstallDir('cmake')) |
| shutil.copytree(os.path.join(SCRIPT_DIR, 'cmake'), GetInstallDir('cmake')) |
| |
| shutil.copy2(os.path.join(SCRIPT_DIR, 'wasi.js'), GetInstallDir()) |
| |
| |
| def ArchiveBinaries(): |
| buildbot.Step('Archive binaries') |
| archive = Archive(GetInstallDir(), print_content=buildbot.IsBot()) |
| if not buildbot.IsUploadingBot(): |
| return |
| # All relevant binaries were copied to the LLVM directory. |
| UploadArchive('binaries', archive) |
| |
| |
| def DebianPackage(): |
| if not (IsLinux() and buildbot.IsBot()): |
| return |
| |
| buildbot.Step('Debian package') |
| top_dir = os.path.dirname(SCRIPT_DIR) |
| try: |
| if buildbot.BuildNumber(): |
| message = ('Automatic build %s produced on http://wasm-stat.us' % |
| buildbot.BuildNumber()) |
| version = '0.1.' + buildbot.BuildNumber() |
| proc.check_call(['dch', '-D', 'unstable', '-v', version, message], |
| cwd=top_dir) |
| proc.check_call(['debuild', '--no-lintian', '-i', '-us', '-uc', '-b'], |
| cwd=top_dir) |
| if buildbot.BuildNumber(): |
| proc.check_call(['git', 'checkout', 'debian/changelog'], |
| cwd=top_dir) |
| |
| debfile = os.path.join(os.path.dirname(top_dir), |
| 'wasm-toolchain_%s_amd64.deb' % version) |
| UploadFile(debfile, os.path.basename(debfile)) |
| except proc.CalledProcessError: |
| # Note the failure but allow the build to continue. |
| buildbot.Fail() |
| return |
| |
| |
| def CompileLLVMTorture(outdir, opt): |
| name = 'Compile LLVM Torture (%s)' % opt |
| buildbot.Step(name) |
| install_bin = GetInstallDir('bin') |
| cc = Executable(os.path.join(install_bin, 'wasm32-wasi-clang')) |
| cxx = Executable(os.path.join(install_bin, 'wasm32-wasi-clang++')) |
| Remove(outdir) |
| Mkdir(outdir) |
| unexpected_result_count = compile_torture_tests.run( |
| cc=cc, |
| cxx=cxx, |
| testsuite=GccTestDir(), |
| sysroot_dir=GetInstallDir('sysroot'), |
| fails=[ |
| GetLLVMSrcDir('llvm', 'lib', 'Target', 'WebAssembly', IT_IS_KNOWN) |
| ], |
| exclusions=LLVM_TORTURE_EXCLUSIONS, |
| out=outdir, |
| config='clang', |
| opt=opt) |
| if 0 != unexpected_result_count: |
| buildbot.Fail() |
| |
| |
| def CompileLLVMTortureEmscripten(name, em_config, outdir, fails, opt): |
| buildbot.Step('Compile LLVM Torture (%s, %s)' % (name, opt)) |
| cc = Executable(GetInstallDir('emscripten', 'emcc'), '.bat') |
| cxx = Executable(GetInstallDir('emscripten', 'em++'), '.bat') |
| Remove(outdir) |
| Mkdir(outdir) |
| os.environ['EM_CONFIG'] = em_config |
| unexpected_result_count = compile_torture_tests.run( |
| cc=cc, |
| cxx=cxx, |
| testsuite=GccTestDir(), |
| sysroot_dir=GetInstallDir('sysroot'), |
| fails=fails, |
| exclusions=LLVM_TORTURE_EXCLUSIONS, |
| out=outdir, |
| config='emscripten', |
| opt=opt) |
| |
| if 0 != unexpected_result_count: |
| buildbot.Fail() |
| |
| |
| def LinkLLVMTorture(name, linker, fails, indir, outdir, extension, |
| opt, args=None): |
| buildbot.Step('Link LLVM Torture (%s, %s)' % (name, opt)) |
| assert os.path.isfile(linker), 'Cannot find linker at %s' % linker |
| Remove(outdir) |
| Mkdir(outdir) |
| input_pattern = os.path.join(indir, '*.' + extension) |
| unexpected_result_count = link_assembly_files.run(linker=linker, |
| files=input_pattern, |
| fails=fails, |
| attributes=[opt], |
| out=outdir, |
| args=args) |
| if 0 != unexpected_result_count: |
| buildbot.Fail() |
| |
| |
| def ExecuteLLVMTorture(name, runner, indir, fails, attributes, extension, opt, |
| outdir='', wasmjs='', extra_files=None, |
| warn_only=False): |
| extra_files = [] if extra_files is None else extra_files |
| |
| buildbot.Step('Execute LLVM Torture (%s, %s)' % (name, opt)) |
| if not indir: |
| print('Step skipped: no input') |
| buildbot.Warn() |
| return None |
| assert os.path.isfile(runner), 'Cannot find runner at %s' % runner |
| files = os.path.join(indir, '*.%s' % extension) |
| if len(glob.glob(files)) == 0: |
| print("No files found by", files) |
| buildbot.Fail() |
| return |
| unexpected_result_count = execute_files.run(runner=runner, |
| files=files, |
| fails=fails, |
| attributes=attributes + [opt], |
| out=outdir, |
| wasmjs=wasmjs, |
| extra_files=extra_files) |
| if 0 != unexpected_result_count: |
| buildbot.FailUnless(lambda: warn_only) |
| |
| |
| def ValidateLLVMTorture(indir, ext, opt): |
| validate = Executable(os.path.join(GetInstallDir('bin'), 'wasm-validate')) |
| # Object files contain a DataCount section, so enable bulk memory |
| ExecuteLLVMTorture(name='validate', |
| runner=validate, |
| indir=indir, |
| fails=None, |
| attributes=[opt], |
| extension=ext, |
| opt=opt) |
| |
| |
| class Build(object): |
| def __init__(self, name_, runnable_, os_filter=None, is_default=True, |
| *args, **kwargs): |
| self.name = name_ |
| self.runnable = runnable_ |
| self.os_filter = os_filter |
| self.is_deafult = is_default |
| self.args = args |
| self.kwargs = kwargs |
| |
| def Run(self): |
| if self.os_filter and not self.os_filter.Check(BuilderPlatformName()): |
| print("Skipping %s: Doesn't work on %s" % |
| (self.runnable.__name__, BuilderPlatformName())) |
| return |
| self.runnable(*self.args, **self.kwargs) |
| |
| |
| def Summary(): |
| buildbot.Step('Summary') |
| |
| # Emscripten-releases bots run the stages separately so LKGR has no way of |
| # knowing whether everything passed or not. |
| should_upload = (buildbot.IsUploadingBot() and |
| not buildbot.IsEmscriptenReleasesBot()) |
| |
| if should_upload: |
| info = {'repositories': GetRepoInfo()} |
| info['build'] = buildbot.BuildNumber() |
| info['scheduler'] = buildbot.Scheduler() |
| info_file = GetInstallDir('buildinfo.json') |
| info_json = json.dumps(info, indent=2) |
| print(info_json) |
| |
| with open(info_file, 'w+') as f: |
| f.write(info_json) |
| f.write('\n') |
| |
| print('Failed steps: %s.' % buildbot.Failed()) |
| for step in buildbot.FailedList(): |
| print(' %s' % step) |
| print('Warned steps: %s.' % buildbot.Warned()) |
| for step in buildbot.WarnedList(): |
| print(' %s' % step) |
| |
| if should_upload: |
| latest_file = '%s/%s' % (buildbot.BuilderName(), 'latest.json') |
| buildbot.Link('latest.json', cloud.Upload(info_file, latest_file)) |
| |
| if buildbot.Failed(): |
| buildbot.Fail() |
| else: |
| if should_upload: |
| lkgr_file = '%s/%s' % (buildbot.BuilderName(), 'lkgr.json') |
| buildbot.Link('lkgr.json', cloud.Upload(info_file, lkgr_file)) |
| |
| |
| def AllBuilds(): |
| return [ |
| # Host tools |
| Build('llvm', LLVM), |
| Build('llvm-test-depends', LLVMTestDepends), |
| Build('v8', V8, os_filter=Filter(exclude=['mac'])), |
| Build('jsvu', Jsvu, os_filter=Filter(exclude=['windows'])), |
| Build('wabt', Wabt), |
| Build('binaryen', Binaryen), |
| Build('emscripten-upstream', Emscripten), |
| # Target libs |
| # TODO: re-enable wasi on windows, see #517 |
| Build('wasi-libc', WasiLibc, os_filter=Filter(exclude=['windows'])), |
| Build('compiler-rt', CompilerRT, |
| os_filter=Filter(exclude=['windows'])), |
| Build('libcxx', LibCXX, os_filter=Filter(exclude=['windows'])), |
| Build('libcxxabi', LibCXXABI, os_filter=Filter(exclude=['windows'])), |
| # Archive |
| Build('archive', ArchiveBinaries), |
| Build('debian', DebianPackage), |
| ] |
| |
| |
| # For now, just the builds used to test WASI and emscripten torture tests |
| # on wasm-stat.us |
| DEFAULT_BUILDS = [ |
| 'llvm', 'v8', 'jsvu', 'wabt', 'binaryen', |
| 'emscripten-upstream', 'wasi-libc', 'compiler-rt', |
| 'libcxx', 'libcxxabi', 'archive' |
| ] |
| |
| |
| def BuildRepos(filter): |
| for rule in filter.Apply(AllBuilds()): |
| rule.Run() |
| |
| |
| class Test(object): |
| def __init__(self, name_, runnable_, os_filter=None): |
| self.name = name_ |
| self.runnable = runnable_ |
| self.os_filter = os_filter |
| |
| def Test(self): |
| if self.os_filter and not self.os_filter.Check(BuilderPlatformName()): |
| print("Skipping %s: Doesn't work on %s" % |
| (self.name, BuilderPlatformName())) |
| return |
| self.runnable() |
| |
| |
| def GetTortureDir(name, opt): |
| dirs = { |
| 'asm2wasm': GetTestDir('asm2wasm-torture-out', opt), |
| 'emwasm': GetTestDir('emwasm-torture-out', opt), |
| } |
| if name in dirs: |
| return dirs[name] |
| return GetTestDir('torture-' + name, opt) |
| |
| |
| def TestBare(): |
| # Compile |
| for opt in BARE_TEST_OPT_FLAGS: |
| CompileLLVMTorture(GetTortureDir('o', opt), opt) |
| ValidateLLVMTorture(GetTortureDir('o', opt), 'o', opt) |
| |
| # Link/Assemble |
| for opt in BARE_TEST_OPT_FLAGS: |
| LinkLLVMTorture(name='lld', |
| linker=Executable( |
| GetInstallDir('bin', 'wasm32-wasi-clang++')), |
| fails=LLD_KNOWN_TORTURE_FAILURES, |
| indir=GetTortureDir('o', opt), |
| outdir=GetTortureDir('lld', opt), |
| extension='o', |
| opt=opt) |
| |
| # Execute |
| common_attrs = ['bare'] |
| common_attrs += ['win'] if IsWindows() else ['posix'] |
| |
| # Avoid d8 execution on windows because of flakiness, |
| # https://bugs.chromium.org/p/v8/issues/detail?id=8211 |
| if not IsWindows(): |
| for opt in BARE_TEST_OPT_FLAGS: |
| ExecuteLLVMTorture(name='d8', |
| runner=D8Bin(), |
| indir=GetTortureDir('lld', opt), |
| fails=RUN_KNOWN_TORTURE_FAILURES, |
| attributes=common_attrs + ['d8', 'lld', opt], |
| extension='wasm', |
| opt=opt, |
| wasmjs=os.path.join(SCRIPT_DIR, 'wasi.js')) |
| |
| if IsMac() and not buildbot.DidStepFailOrWarn('jsvu'): |
| for opt in BARE_TEST_OPT_FLAGS: |
| ExecuteLLVMTorture(name='jsc', |
| runner=os.path.join(JSVU_OUT_DIR, 'jsc'), |
| indir=GetTortureDir('lld', opt), |
| fails=RUN_KNOWN_TORTURE_FAILURES, |
| attributes=common_attrs + ['jsc', 'lld'], |
| extension='wasm', |
| opt=opt, |
| warn_only=True, |
| wasmjs=os.path.join(SCRIPT_DIR, 'wasi.js')) |
| |
| |
| def TestEmwasm(): |
| for opt in EMSCRIPTEN_TEST_OPT_FLAGS: |
| CompileLLVMTortureEmscripten('emwasm', |
| GetInstallDir(EMSCRIPTEN_CONFIG_UPSTREAM), |
| GetTortureDir('emwasm', opt), |
| EMWASM_KNOWN_TORTURE_COMPILE_FAILURES, |
| opt) |
| |
| # Avoid d8 execution on windows because of flakiness, |
| # https://bugs.chromium.org/p/v8/issues/detail?id=8211 |
| if not IsWindows(): |
| for opt in EMSCRIPTEN_TEST_OPT_FLAGS: |
| ExecuteLLVMTorture(name='emwasm', |
| runner=D8Bin(), |
| indir=GetTortureDir('emwasm', opt), |
| fails=RUN_KNOWN_TORTURE_FAILURES, |
| attributes=['emwasm', 'lld', 'd8'], |
| extension='c.js', |
| opt=opt, |
| outdir=GetTortureDir('emwasm', opt)) |
| |
| |
| def ExecuteEmscriptenTestSuite(name, tests, config, outdir, warn_only=False): |
| buildbot.Step('Execute emscripten testsuite (%s)' % name) |
| Mkdir(outdir) |
| |
| # Before we can run the tests we prepare the installed emscripten |
| # directory by copying of some test data which is otherwise excluded by |
| # emscripten install script (tools/install.py). |
| em_install_dir = GetInstallDir('emscripten') |
| installed_tests = os.path.join(em_install_dir, 'tests', 'third_party') |
| if not os.path.exists(installed_tests): |
| src_dir = GetSrcDir('emscripten', 'tests', 'third_party') |
| print('Copying directory %s to %s' % (src_dir, em_install_dir)) |
| shutil.copytree(src_dir, installed_tests) |
| |
| cmd = [ |
| GetInstallDir('emscripten', 'tests', 'runner.py'), |
| '--em-config', config |
| ] + tests |
| test_env = os.environ.copy() |
| if buildbot.IsBot() and IsWindows(): |
| test_env['EMTEST_LACKS_NATIVE_CLANG'] = '1' |
| try: |
| proc.check_call(cmd, cwd=outdir, env=test_env) |
| except proc.CalledProcessError: |
| buildbot.FailUnless(lambda: warn_only) |
| |
| |
| def TestEmtest(): |
| tests = options.test_params if options.test_params else ['wasm2', 'other'] |
| ExecuteEmscriptenTestSuite('emwasm', tests, |
| GetInstallDir(EMSCRIPTEN_CONFIG_UPSTREAM), |
| os.path.join(work_dirs.GetTest(), 'emtest-out')) |
| |
| |
| def TestLLVMTestSuite(): |
| buildbot.Step('Execute LLVM TestSuite') |
| |
| outdir = GetBuildDir('llvmtest-out') |
| # The compiler changes on every run, so incremental builds don't make |
| # sense. |
| Remove(outdir) |
| Mkdir(outdir) |
| # The C++ tests explicitly link libstdc++ for some reason, but we use |
| # libc++ and it's unnecessary to link it anyway. So create an empty |
| # libstdc++.a |
| proc.check_call([GetInstallDir('bin', 'llvm-ar'), 'rc', 'libstdc++.a'], |
| cwd=outdir) |
| # This has to be in the environment and not TEST_SUITE_EXTRA_C_FLAGS |
| # because CMake doesn't append the flags to the try-compiles. |
| os.environ['EM_CONFIG'] = GetInstallDir(EMSCRIPTEN_CONFIG_UPSTREAM) |
| command = [GetInstallDir('emscripten', 'emcmake')] + CMakeCommandBase() + [ |
| GetSrcDir('llvm-test-suite'), '-DCMAKE_C_COMPILER=' + |
| GetInstallDir('emscripten', 'emcc'), '-DCMAKE_CXX_COMPILER=' + |
| GetInstallDir('emscripten', 'em++'), '-DTEST_SUITE_RUN_UNDER=' + |
| NodeBin(), '-DTEST_SUITE_USER_MODE_EMULATION=ON', |
| '-DTEST_SUITE_SUBDIRS=SingleSource', |
| '-DTEST_SUITE_EXTRA_EXE_LINKER_FLAGS=' + |
| '-L %s -s TOTAL_MEMORY=1024MB' % outdir, |
| '-DTEST_SUITE_LLVM_SIZE=' + GetInstallDir('emscripten', 'emsize.py') |
| ] |
| |
| proc.check_call(command, cwd=outdir) |
| proc.check_call(['ninja', '-v'], cwd=outdir) |
| results_file = 'results.json' |
| lit = GetBuildDir('llvm-out', 'bin', 'llvm-lit') |
| proc.call([lit, '-v', '-o', results_file, '.'], cwd=outdir) |
| |
| with open(os.path.join(outdir, results_file)) as results_fd: |
| json_results = json.loads(results_fd.read()) |
| |
| def get_names(code): |
| # Strip the unneccessary spaces from the test name |
| return [ |
| r['name'].replace('test-suite :: ', '') |
| for r in json_results['tests'] if r['code'] == code |
| ] |
| |
| failures = get_names('FAIL') |
| successes = get_names('PASS') |
| |
| expected_failures = testing.parse_exclude_files( |
| RUN_LLVM_TESTSUITE_FAILURES, []) |
| unexpected_failures = [f for f in failures if f not in expected_failures] |
| unexpected_successes = [f for f in successes if f in expected_failures] |
| |
| if len(unexpected_failures) > 0: |
| print('Emscripten unexpected failures:') |
| for test in unexpected_failures: |
| print(test) |
| if len(unexpected_successes) > 0: |
| print('Emscripten unexpected successes:') |
| for test in unexpected_successes: |
| print(test) |
| |
| if len(unexpected_failures) + len(unexpected_successes) > 0: |
| buildbot.Fail() |
| |
| |
| ALL_TESTS = [ |
| Test('llvm-regression', TestLLVMRegression), |
| # TODO: re-enable wasi on windows, see #517 |
| Test('bare', TestBare, Filter(exclude=['windows'])), |
| Test('emwasm', TestEmwasm, Filter(exclude=['mac'])), |
| # These tests do have interesting differences on OSes (especially the |
| # 'other' tests) and eventually should run everywhere. |
| Test('emtest', TestEmtest), |
| Test('llvmtest', TestLLVMTestSuite, Filter(include=['linux'])), |
| ] |
| |
| # The default tests to run on wasm-stat.us (just WASI and emwasm torture) |
| DEFAULT_TESTS = ['bare', 'emwasm', 'llvmtest'] |
| |
| |
| def TextWrapNameList(prefix, items): |
| width = 80 # TODO(binji): better guess? |
| names = sorted(item.name for item in items) |
| return '%s%s' % (prefix, |
| textwrap.fill(' '.join(names), |
| width, |
| initial_indent=' ', |
| subsequent_indent=' ')) |
| |
| |
| def ParseArgs(): |
| def SplitComma(arg): |
| if not arg: |
| return None |
| return arg.split(',') |
| |
| epilog = '\n\n'.join([ |
| TextWrapNameList('sync targets:\n', AllSources()), |
| TextWrapNameList('build targets:\n', AllBuilds()), |
| TextWrapNameList('test targets:\n', ALL_TESTS), |
| ]) |
| |
| parser = argparse.ArgumentParser( |
| description='Wasm waterfall top-level CI script', |
| formatter_class=argparse.RawDescriptionHelpFormatter, |
| epilog=epilog) |
| |
| parser.add_argument( |
| '--sync-dir', dest='sync_dir', help='Directory for syncing sources') |
| parser.add_argument( |
| '--build-dir', dest='build_dir', help='Directory for build output') |
| parser.add_argument( |
| '--prebuilt-dir', dest='prebuilt_dir', |
| help='Directory for prebuilt output') |
| parser.add_argument( |
| '--v8-dir', dest='v8_dir', |
| help='Directory for V8 checkout/build') |
| parser.add_argument( |
| '--test-dir', dest='test_dir', help='Directory for test output') |
| parser.add_argument( |
| '--install-dir', dest='install_dir', |
| help='Directory for installed output') |
| |
| sync_grp = parser.add_mutually_exclusive_group() |
| sync_grp.add_argument( |
| '--no-sync', dest='sync', default=True, action='store_false', |
| help='Skip fetching and checking out source repos') |
| sync_grp.add_argument( |
| '--sync-include', dest='sync_include', default='', type=SplitComma, |
| help='Include only the comma-separated list of sync targets') |
| sync_grp.add_argument( |
| '--sync-exclude', dest='sync_exclude', default='', type=SplitComma, |
| help='Exclude the comma-separated list of sync targets') |
| |
| parser.add_argument( |
| '--sync-lkgr', dest='sync_lkgr', default=False, action='store_true', |
| help='When syncing, only sync up to the Last Known Good Revision ' |
| 'for each sync target') |
| |
| build_grp = parser.add_mutually_exclusive_group() |
| build_grp.add_argument( |
| '--no-build', dest='build', default=True, action='store_false', |
| help='Skip building source repos (also skips V8 and LLVM unit tests)') |
| build_grp.add_argument( |
| '--build-include', dest='build_include', default='', type=SplitComma, |
| help='Include only the comma-separated list of build targets') |
| build_grp.add_argument( |
| '--build-exclude', dest='build_exclude', default='', type=SplitComma, |
| help='Exclude the comma-separated list of build targets') |
| |
| test_grp = parser.add_mutually_exclusive_group() |
| test_grp.add_argument( |
| '--no-test', dest='test', default=True, action='store_false', |
| help='Skip running tests') |
| test_grp.add_argument( |
| '--test-include', dest='test_include', default='', type=SplitComma, |
| help='Include only the comma-separated list of test targets') |
| test_grp.add_argument( |
| '--test-exclude', dest='test_exclude', default='', type=SplitComma, |
| help='Exclude the comma-separated list of test targets') |
| parser.add_argument( |
| '--test-params', dest='test_params', default='', type=SplitComma, |
| help='Test selector to pass through to emscripten testsuite runner') |
| |
| parser.add_argument( |
| '--no-threads', action='store_true', |
| help='Disable use of thread pool to building and testing') |
| parser.add_argument( |
| '--torture-filter', |
| help='Limit which torture tests are run by applying the given glob') |
| parser.add_argument( |
| '--git-status', dest='git_status', default=False, action='store_true', |
| help='Show git status for each sync target. ' |
| "Doesn't sync, build, or test") |
| parser.add_argument( |
| '--no-host-clang', dest='host_clang', action='store_false', |
| help="Don't force chrome clang as the host compiler") |
| parser.add_argument( |
| '--no-sysroot', dest='use_sysroot', action='store_false', |
| help="Don't use the V8 sysroot to build on Linux") |
| parser.add_argument( |
| '--clobber', dest='clobber', default=False, action='store_true', |
| help="Delete working directories, forcing a clean build") |
| parser.add_argument( |
| '--use-lto', dest='use_lto', default=False, action='store_true', |
| help='Use extra optimization for host binaries') |
| |
| return parser.parse_args() |
| |
| |
| def AddToPath(path): |
| print("adding to path: %s" % path) |
| os.environ['PATH'] = path + os.pathsep + os.environ['PATH'] |
| |
| |
| def run(sync_filter, build_filter, test_filter): |
| if options.git_status: |
| for s in AllSources(): |
| s.PrintGitStatus() |
| return 0 |
| |
| Clobber() |
| Chdir(SCRIPT_DIR) |
| for work_dir in work_dirs.GetAll(): |
| Mkdir(work_dir) |
| SyncRepos(sync_filter, options.sync_lkgr) |
| if build_filter.All(): |
| Remove(GetInstallDir()) |
| Mkdir(GetInstallDir()) |
| Mkdir(GetInstallDir('bin')) |
| Mkdir(GetInstallDir('lib')) |
| |
| # Add prebuilt cmake to PATH so any subprocesses use a consistent cmake. |
| AddToPath(os.path.dirname(PrebuiltCMakeBin())) |
| |
| # `npm` uses whatever `node` is in `PATH`. To make sure it uses the |
| # Node.js version we want, we prepend the node bin dir to `PATH`. |
| AddToPath(NodeBinDir()) |
| |
| # TODO(dschuff): Figure out how to make these statically linked? |
| if IsWindows() and build_filter.Any(): |
| host_toolchains.CopyDlls(GetInstallDir('bin'), 'Debug') |
| |
| try: |
| BuildRepos(build_filter) |
| except Exception: |
| # If any exception reaches here, do not attempt to run the tests; just |
| # log the error for buildbot and exit |
| print("Exception thrown in build step.") |
| traceback.print_exc() |
| buildbot.Fail() |
| Summary() |
| return 1 |
| |
| # Override the default locale to use UTF-8 encoding for all files and stdio |
| # streams (see PEP540), since oure test files are encoded with UTF-8. |
| os.environ['PYTHONUTF8'] = '1' |
| for t in test_filter.Apply(ALL_TESTS): |
| t.Test() |
| |
| # Keep the summary step last: it'll be marked as red if the return code is |
| # non-zero. Individual steps are marked as red with buildbot.Fail(). |
| Summary() |
| return buildbot.Failed() |
| |
| |
| def main(): |
| global options |
| start = time.time() |
| options = ParseArgs() |
| print('Python version %s' % sys.version) |
| |
| if options.no_threads: |
| testing.single_threaded = True |
| if options.torture_filter: |
| compile_torture_tests.test_filter = options.torture_filter |
| |
| if options.sync_dir: |
| work_dirs.SetSync(options.sync_dir) |
| if options.build_dir: |
| work_dirs.SetBuild(options.build_dir) |
| if options.v8_dir: |
| work_dirs.SetV8(options.v8_dir) |
| if options.test_dir: |
| work_dirs.SetTest(options.test_dir) |
| if options.install_dir: |
| work_dirs.SetInstall(options.install_dir) |
| if options.prebuilt_dir: |
| work_dirs.SetPrebuilt(options.prebuilt_dir) |
| if not options.host_clang: |
| host_toolchains.SetForceHostClang(False) |
| if not options.use_sysroot: |
| host_toolchains.SetUseSysroot(False) |
| |
| sync_include = options.sync_include if options.sync else [] |
| sync_filter = Filter('sync', sync_include, options.sync_exclude) |
| build_include = [] if not options.build else ( |
| options.build_include if options.build_include else DEFAULT_BUILDS) |
| build_filter = Filter('build', build_include, options.build_exclude) |
| test_include = [] if not options.test else ( |
| options.test_include if options.test_include else DEFAULT_TESTS) |
| test_filter = Filter('test', test_include, options.test_exclude) |
| |
| try: |
| ret = run(sync_filter, build_filter, test_filter) |
| print('Completed in {}s'.format(time.time() - start)) |
| return ret |
| except: # noqa |
| traceback.print_exc() |
| # If an except is raised during one of the steps we still need to |
| # print the @@@STEP_FAILURE@@@ annotation otherwise the annotator |
| # makes the failed stap as green: |
| # TODO(sbc): Remove this if the annotator is fixed: |
| # http://crbug.com/647357 |
| if buildbot.current_step: |
| buildbot.Fail() |
| return 1 |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |