summaryrefslogtreecommitdiffstats
path: root/native_client_sdk/src/main.scons
diff options
context:
space:
mode:
Diffstat (limited to 'native_client_sdk/src/main.scons')
-rw-r--r--native_client_sdk/src/main.scons1065
1 files changed, 1065 insertions, 0 deletions
diff --git a/native_client_sdk/src/main.scons b/native_client_sdk/src/main.scons
new file mode 100644
index 0000000..557333d
--- /dev/null
+++ b/native_client_sdk/src/main.scons
@@ -0,0 +1,1065 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+""" Main scons script for Native Client SDK builds.
+
+Do not invoke this script directly, but instead use the scons or scons.bat
+wrapper function. E.g.
+
+Linux or Mac:
+ ./scons [Options...]
+
+Windows:
+ scons.bat [Options...]
+"""
+
+from __future__ import with_statement
+
+import os
+import platform
+import subprocess
+import sys
+import toolchainbinaries
+from build_tools import build_utils
+
+# ----------------------------------------------------------------------------
+HELP_STRING = """
+===============================================================================
+Help for NaCl SDK
+===============================================================================
+
+* cleaning: ./scons -c
+* build a target: ./scons <target>
+
+Supported targets:
+ * bot Runs everything that the build and try bots run.
+ * debug_server_x64 Build the out-of-process debug_server.
+ * debug_server_Win32 Build the out-of-process debug_server.
+ * docs Build all of the Doxygen documentation.
+ * examples Build the examples.
+ * experimental Build the experimental projects.
+ * installer Build the SDK installer.
+ * nacl-bpad Build the native client crash reporting tool.
+ * sdk_tools Build nacl_sdk.zip and sdk_tools.tgz
+ * toolchain Update the toolchain's headers and libraries.
+ * vsx Build the Visual Studio Plugin.
+
+Flags:
+ * USE_EXISTING_INSTALLER=1 Do not rebuild the installer if it exists.
+ * chrome_browser_path=<full_path> Download Chrome to <full_path>.
+ * SHOW_BROWSER=1 Don't suppress browser GUI while performing
+ browser tests in Linux.
+
+More targets are listed below in the automatically generated help section.
+
+===============================================================================
+Automatically generated help follows:
+===============================================================================
+"""
+
+# ----------------------------------------------------------------------------
+# Perform some environment checks before running.
+# Note that scons should set NACL_SDK_ROOT before this script runs.
+
+if os.getenv('NACL_SDK_ROOT') is None:
+ sys.stderr.write('NACL_SDK_ROOT must be defined as the root directory'
+ ' of NaCl SDK.\n')
+ sys.exit(1)
+
+# By default, run with a parallel build (i.e. '-j num_jobs').
+# Use a default value proportional to the number of cpu cores on the system.
+# To run a serial build, explicitly type '-j 1' on the command line.
+try:
+ import multiprocessing
+ CORE_COUNT = multiprocessing.cpu_count()
+except (ImportError, NotImplementedError):
+ CORE_COUNT = 2 # Our buildbots seem to be dual-core typically
+
+SetOption('num_jobs', CORE_COUNT * 2)
+print 'Building with', GetOption('num_jobs'), 'parallel jobs'
+
+# ----------------------------------------------------------------------------
+# The environment_list contains all the build environments that we want to
+# specify. Selecting a particular environment is done using the --mode option.
+# Each environment that we support gets appended to this list.
+environment_list = []
+
+# ----------------------------------------------------------------------------
+# Create the base environment, from which all other environments are derived.
+base_env = Environment(
+ tools = ['component_setup'],
+ CPPPATH = ['$MAIN_DIR'],
+ CPPDEFINES = [
+ 'BOOST_ALL_NO_LIB',
+ ],
+ NACL_TOOLCHAIN_ROOTS = {
+ ('x86', 'newlib'):
+ build_utils.NormalizeToolchain(arch='x86', variant='newlib'),
+ ('x86', 'glibc'):
+ build_utils.NormalizeToolchain(arch='x86', variant='glibc'),
+ },
+ ROOT_DIR = os.path.abspath(os.getcwd()),
+ IS_WINDOWS = sys.platform in ['cygwin', 'win32'],
+ IS_LINUX = sys.platform == 'linux2',
+ IS_MAC = sys.platform == 'darwin',
+ JOB_COUNT = GetOption('num_jobs')
+)
+
+# It is possible to override these values on the command line by typing
+# something like this:
+# PYTHON=/path/to/my/python
+base_env.SetDefault(
+ PYTHON = ARGUMENTS.get('PYTHON', 'python'),
+ USE_EXISTING_INSTALLER = ARGUMENTS.get('USE_EXISTING_INSTALLER', False),
+ SHOW_BROWSER = ARGUMENTS.get('SHOW_BROWSER', False),
+)
+
+base_env.Append(
+ BUILD_SCONSCRIPTS = [
+ # Keep in alphabetical order
+ 'build_tools/build.scons',
+ 'debugger/build.scons',
+ 'documentation/build.scons',
+ 'experimental/visual_studio_plugin/build.scons',
+ 'experimental/webgtt/tests/nacltest/test.scons',
+ 'project_templates/test.scons',
+ ],
+)
+
+base_env.Help(HELP_STRING)
+
+KNOWN_SUITES = frozenset([
+ 'bot',
+ ])
+
+
+def HasBotTarget(env):
+ if 'bot' in COMMAND_LINE_TARGETS:
+ return True
+ return False
+
+base_env.AddMethod(HasBotTarget)
+
+
+def CheckSuiteName(suite, node_name):
+ '''Check whether a given test suite or alias name is a known name.
+
+ If the suite name is not in the approved list, then this function throws
+ an exception, with the node_name within the error message.
+
+ Args:
+ suite: a name of a suite that must be in the KNOWN_SUITES set
+ node_name: The name of the node. This is used for error messages
+ '''
+ if suite not in KNOWN_SUITES:
+ raise Exception('Testsuite/Alias "%s" for target "%s" is unknown' %
+ (suite, node_name))
+
+
+def AddNodeToTestSuite(env, node, suite_names, node_name, test_size='all'):
+ '''Adds a test node to a given set of suite names
+
+ These tests are automatically added to the run_all_tests target and are
+ listed in the help screen.
+
+ This function is loosely based on a function of the same name in the
+ Native Client repository
+
+ Args:
+ env - The environment from which this function was called
+ node - A scons node (e.g., file, command, etc) to be added to set suite
+ suite_names - A list of test suite names. For none, pass an empty list
+ node_name - The target name used for running this test
+ test_size - The relative run-time of this test: small, medium, or large
+ '''
+
+ # CommandTest can return an empty list when it silently discards a test
+ if not node:
+ return
+
+ AlwaysBuild(node)
+
+ for s in suite_names:
+ CheckSuiteName(s, node_name)
+ env.Alias(s, node)
+
+ if test_size not in ['small', 'medium', 'large', 'all']:
+ raise Exception('Invalid test size for %s' % node_name)
+
+ # Note that COMPONENT_TEST_SIZE is set to 'large' by default, which
+ # populates a largely redundant list of 'large' tests. Note that all
+ # tests are added to 'all', so setting test_size='all' is a no-op
+ env.ComponentTestOutput(node_name, node, COMPONENT_TEST_SIZE=test_size)
+
+base_env.AddMethod(AddNodeToTestSuite)
+
+
+def ShouldBeCleaned(env, targets, suite_names, node_name):
+ '''Determines whether a given set of targets require cleaning.
+
+ Args:
+ env - The calling environment.
+ targets - Any build artifacts to which a cleaning step might apply.
+ Any false object indicates that this check is skipped.
+ suite_names - Any suites that might produce |targets|
+ node_name - A node that might produce |targets|
+ '''
+ if not env.GetOption('clean'):
+ return False
+
+ if len(COMMAND_LINE_TARGETS) > 0:
+ clean_this = False
+ for cl_target in COMMAND_LINE_TARGETS:
+ if cl_target in suite_names or cl_target == node_name:
+ clean_this = True
+ break
+ if not clean_this:
+ return False
+
+ if not targets:
+ return True
+ for target in targets:
+ if os.path.exists(target):
+ return True
+ return False
+
+
+def AddCleanAction(env, targets, action, suite_names, node_name):
+ '''Adds a cleanup action that scons cannot detect automatically.
+
+ Cleaning will only occur if there is a match between the suite or nodes
+ specified on the command line, and suite_names or node_name or if no
+ suite or nodes are specified on the command line. Also, at least one of the
+ targets must exist on the file system.
+
+ Args:
+ env - The calling environment
+ targets - Artifacts to be cleaned.
+ action - The action to be performed. It is up to the caller to ensure
+ that |action| will actually remove |targets|
+ suite_names - Any suites to which this cleanup target applies.
+ node_name - Any nodes to which this cleanup target applies.
+ '''
+ if ShouldBeCleaned(env, targets, suite_names, node_name):
+ env.Execute(action)
+
+base_env.AddMethod(AddCleanAction)
+
+
+def AddNodeAliases(env, node, suite_names, node_name):
+ '''Allow a given node to be built under a different name or as a suite
+
+ Args:
+ env - The calling environment
+ node - A target node to add to a known build alias (e.g., 'bot')
+ suite_names - A list of suite names. For none, pass an empty list. This
+ node will be run whenever any of these suites are invoked.
+ Each suite name must match a string in KNOWN_SUITES.
+ node_name - The name of this node, when run by itself
+ '''
+
+ if not node:
+ return
+
+ for s in suite_names:
+ CheckSuiteName(s, node_name)
+ env.Alias(s, node)
+
+ env.Alias(node_name, node)
+
+base_env.AddMethod(AddNodeAliases)
+
+
+def CreatePythonUnitTest(env, filename, dependencies=None, disabled=False,
+ params=None, buffered=True, banner=None):
+ """Returns a new build command that will run a unit test with a given file.
+
+ Args:
+ env: SCons environment
+ filename: The python file that contains the unit test
+ dependencies: An optional list of other files that this unit test uses
+ disabled: Setting this to True will prevent the test from running
+ params: Optional additional parameters for python command
+ buffered: True=stdout is buffered until entirely complete;
+ False=stdout is immediately displayed as it occurs.
+ banner: (optional) annotation banner for build/try bots
+
+ Returns:
+ A SCons command node
+ """
+ dependencies = dependencies or []
+ params = params or []
+
+ basename = os.path.splitext(os.path.basename(filename))[0]
+ outfilename = "%s_output.txt" % basename
+
+
+ def RunPythonUnitTest(env, target, source):
+ """Runs unit tests using the given target as a command.
+
+ The argument names of this test are not very intuitive but match what is
+ used conventionally throughout scons. If the string "PASSED" does not
+ occur in target when this exits, the test has failed; also a scons
+ convention.
+
+ Args:
+ env: SCons's current environment.
+ target: Where to write the result of the test.
+ source: The command to run as the test.
+
+ Returns:
+ None for good status
+ An error string for bad status
+ """
+ bot = build_utils.BotAnnotator()
+ if banner:
+ bot.BuildStep(banner)
+
+ if disabled:
+ sys.stdout.write("Test %s is disabled.\n" % basename)
+ sys.stdout.flush()
+ return None # return with good status
+
+ import subprocess
+
+ app = [str(env['PYTHON']), str(source[0].abspath)] + map(
+ lambda param: param if type(param) is str else str(param.abspath),
+ params)
+ bot.Print('Running: %s' % app)
+ app_env = os.environ.copy()
+ # We have to do this because scons overrides PYTHONPATH and does
+ # not preserve what is provided by the OS.
+ python_path = [env['ROOT_DIR'], app_env['PYMOX'], app_env['PYTHONPATH']]
+ app_env['PYTHONPATH'] = os.pathsep.join(python_path)
+ ret_val = 'Error: General Test Failure' # Indicates failure, by default
+ target_str = str(target[0])
+ with open(target_str, 'w') as outfile:
+ def Write(str):
+ if buffered:
+ outfile.write(str)
+ outfile.flush()
+ else:
+ sys.stdout.write(str)
+ sys.stdout.flush()
+ Write('\n-----Begin output for Test: %s\n' % basename)
+ if subprocess.call(app, env=app_env,
+ stdout=outfile if buffered else None,
+ stderr=outfile if buffered else None):
+ Write('-----Error: unit test failed\n')
+ ret_val = 'Error: Test Failure in %s' % basename
+ else:
+ ret_val = None # Indicates success
+
+ Write('-----End output for Test: %s\n' % basename)
+ if buffered:
+ with open(target_str, 'r') as resultfile:
+ sys.stdout.write(resultfile.read())
+ sys.stdout.flush()
+
+ if ret_val:
+ bot.BuildStepFailure()
+
+ return ret_val
+
+ cmd = env.Command(outfilename, filename, RunPythonUnitTest)
+ env.Depends(cmd, dependencies)
+ # Create dependencies for all the env.File parameters and other scons nodes
+ for param in params:
+ if type(param) is not str:
+ env.Depends(cmd, param)
+
+ return cmd
+
+base_env.AddMethod(CreatePythonUnitTest)
+
+
+# ----------------------------------------------------------------------------
+# Support for running Chrome. These functions access the construction
+# Environment() to produce a path to Chrome.
+
+# A Dir object representing the directory where the Chrome binaries are kept.
+# You can use chrome_binaries_dir= to set this on the command line. Defaults
+# to chrome_binaries.
+base_env['CHROME_DOWNLOAD_DIR'] = \
+ base_env.Dir(ARGUMENTS.get('chrome_binaries_dir', '#chrome_binaries'))
+
+
+def ChromeArchitectureSpec(env):
+ '''Determine the architecture spec for the Chrome binary.
+
+ The architecture spec is a string that represents the host architecture.
+ Possible values are:
+ x86-32
+ x86-64
+ On Mac and Windows, the architecture spec is always x86-32, because there are
+ no 64-bit version available.
+
+ Returns: An architecture spec string for the host CPU.
+ '''
+ arch, _ = platform.architecture();
+ # On Mac and Windows, always use a 32-bit version of Chrome (64-bit versions
+ # are not available).
+ if env['IS_WINDOWS'] or env['IS_MAC']:
+ arch = 'x86-32'
+ else:
+ arch = 'x86-64' if '64' in arch else 'x86-32'
+ return arch
+
+base_env.AddMethod(ChromeArchitectureSpec)
+
+
+def GetDefaultChromeBinary(env):
+ '''Get a File object that represents a Chrome binary.
+
+ By default, the test infrastructure will download a copy of Chrome that can
+ be used for testing. This method returns a File object that represents the
+ downloaded Chrome binary that can be run by tests. Note that the path to the
+ binary is specific to the host platform, for example the path on Linux
+ is <chrome_dir>/linux/<arch>/chrome, while on Mac it's
+ <chrome_dir>/mac/<arch>/Chromium.app/Contents.MacOS/Chromium.
+
+ Returns: A File object representing the Chrome binary.
+ '''
+ if env['IS_LINUX']:
+ os_name = 'linux'
+ binary = 'chrome'
+ elif env['IS_WINDOWS']:
+ os_name = 'windows'
+ binary = 'chrome.exe'
+ elif env['IS_MAC']:
+ os_name = 'mac'
+ binary = 'Chromium.app/Contents/MacOS/Chromium'
+ else:
+ raise Exception('Unsupported OS')
+
+ return env.File(os.path.join(
+ '${CHROME_DOWNLOAD_DIR}',
+ '%s_%s' % (os_name, env.ChromeArchitectureSpec()),
+ binary))
+
+base_env.AddMethod(GetDefaultChromeBinary)
+
+
+def GetChromeBinary(env):
+ '''Return a File object that represents the downloaded Chrome binary.
+
+ If chrome_browser_path is specified on the command line, then return a File
+ object that represents that path. Otherwise, return a File object
+ representing the default downloaded Chrome (see GetDefaultChromeBinary(),
+ above).
+
+ Returns: A File object representing a Chrome binary.
+ '''
+ return env.File(ARGUMENTS.get('chrome_browser_path',
+ env.GetDefaultChromeBinary()))
+
+base_env.AddMethod(GetChromeBinary)
+
+
+def DependsOnChrome(env, dependency):
+ '''Create a dependency on the download of Chrome.
+
+ Creates a dependency in |env| such that Chrome gets downloaded (if necessary)
+ whenever |dependency| changes. Uses the Chrome downloader scripts built
+ into NaCl; this script expects NaCl to be DEPS'ed into
+ third_party/native_client/native_client.
+
+ The Chrome binary is added as a precious node to the base Environment. If
+ we added it to the build environment env, then downloading chrome would in
+ effect be specified for multiple environments.
+ '''
+ if not hasattr(base_env, 'download_chrome_node'):
+ chrome_binary = env.GetDefaultChromeBinary()
+ download_chrome_script = build_utils.JoinPathToNaClRepo(
+ 'native_client', 'build', 'download_chrome.py',
+ root_dir=env['ROOT_DIR'])
+ base_env.download_chrome_node = env.Command(
+ chrome_binary,
+ [],
+ '${PYTHON} %s --arch=%s --dst=${CHROME_DOWNLOAD_DIR}' %
+ (download_chrome_script, env.ChromeArchitectureSpec()))
+ # This stops Scons from deleting the file before running the step above.
+ env.NoClean(chrome_binary)
+ env.Precious(chrome_binary)
+ env.Depends(dependency, base_env.download_chrome_node)
+
+base_env.AddMethod(DependsOnChrome)
+
+
+# ----------------------------------------------------------------------------
+# Targets for updating sdk headers and libraries
+# NACL_SDK_XXX vars are defined by site_scons/site_tools/naclsdk.py
+# NOTE: Our task here is complicated by the fact that there might already be
+# some (outdated) headers/libraries at the new location
+# One of the hacks we employ here is to make every library depend
+# on the installation on ALL headers (sdk_headers)
+
+# Contains all the headers to be installed
+sdk_headers = base_env.Alias('extra_sdk_update_header', [])
+# Contains all the libraries and .o files to be installed
+libs_platform = base_env.Alias('extra_sdk_libs_platform', [])
+libs = base_env.Alias('extra_sdk_libs', [])
+base_env.Alias('extra_sdk_update', [libs, libs_platform])
+
+AlwaysBuild(sdk_headers)
+
+# ----------------------------------------------------------------------------
+# The following section contains proxy nodes which can be used to create
+# dependencies between targets that are not in the same scope or environment.
+toolchain_node = base_env.Alias('toolchain', [])
+
+
+def GetToolchainNode(env):
+ '''Returns the node associated with the toolchain build target'''
+ return toolchain_node
+
+base_env.AddMethod(GetToolchainNode)
+
+
+def GetHeadersNode(env):
+ return sdk_headers
+
+base_env.AddMethod(GetHeadersNode)
+
+installer_prereqs_node = base_env.Alias('installer_prereqs', [])
+
+
+def GetInstallerPrereqsNode(env):
+ return installer_prereqs_node
+
+base_env.AddMethod(GetInstallerPrereqsNode)
+
+installer_test_node = base_env.Alias('installer_test_node', [])
+
+
+def GetInstallerTestNode(env):
+ return installer_test_node
+
+base_env.AddMethod(GetInstallerTestNode)
+
+
+def AddHeaderToSdk(env, nodes, subdir = 'nacl/', base_dirs = None):
+ """Add a header file to the toolchain. By default, Native Client-specific
+ headers go under nacl/, but there are non-specific headers, such as
+ the OpenGLES2 headers, that go under their own subdir.
+
+ Args:
+ env: Environment in which we were called.
+ nodes: A list of node objects to add to the toolchain
+ subdir: This is appended to each base_dir
+ base_dirs: A list of directories to install the node to"""
+ if not base_dirs:
+ # TODO(mball): This won't work for PNaCl:
+ base_dirs = [os.path.join(dir, 'x86_64-nacl', 'include')
+ for dir in env['NACL_TOOLCHAIN_ROOTS'].values()]
+
+ for base_dir in base_dirs:
+ node = env.Replicate(os.path.join(base_dir, subdir), nodes)
+ env.Depends(sdk_headers, node)
+ env.Depends(toolchain_node, node)
+ return node
+
+base_env.AddMethod(AddHeaderToSdk)
+
+
+def AddLibraryToSdkHelper(env, nodes, is_lib, is_platform):
+ """"Helper function to install libs/objs into the toolchain
+ and associate the action with the extra_sdk_update.
+
+ Args:
+ env: Environment in which we were called.
+ nodes: list of libc/objs
+ is_lib: treat nodes as libs
+ is_platform: nodes are truly platform specific
+ """
+ env.Requires(nodes, sdk_headers)
+
+ dir = ARGUMENTS.get('extra_sdk_lib_destination')
+ if not dir:
+ dir = '${NACL_SDK_LIB}/'
+
+ if is_lib:
+ n = env.ReplicatePublished(dir, nodes, 'link')
+ else:
+ n = env.Replicate(dir, nodes)
+
+ if is_platform:
+ env.Alias('extra_sdk_libs_platform', n)
+ else:
+ env.Alias('extra_sdk_libs', n)
+ return n
+
+
+def AddLibraryToSdk(env, nodes, is_platform=False):
+ return AddLibraryToSdkHelper(env, nodes, True, is_platform)
+
+base_env.AddMethod(AddLibraryToSdk)
+
+
+# ----------------------------------------------------------------------------
+# This is a simple environment that is primarily for targets that aren't built
+# directly by scons, and therefore don't need any special environment setup.
+build_env = base_env.Clone(
+ BUILD_TYPE = 'build',
+ BUILD_GROUPS = ['default', 'all'],
+ BUILD_TYPE_DESCRIPTION = 'Default build environment',
+ HOST_PLATFORMS = '*',
+ )
+
+environment_list.append(build_env)
+
+# ----------------------------------------------------------------------------
+# Get the appropriate build command depending on the environment.
+
+
+def SconsBuildCommand(env):
+ '''Return the build command used to run separate scons instances.
+ Args:
+ env: The construction Environment() that is building using scons.
+ Returns:
+ A string representing the platform-specific build command that will run the
+ scons instances.
+ '''
+ if env['IS_WINDOWS']:
+ return 'scons.bat --jobs=%s' % GetOption('num_jobs')
+ else:
+ return './scons --jobs=%s' % GetOption('num_jobs')
+
+# ----------------------------------------------------------------------------
+# Add a builder for examples. This adds an Alias() node named 'examples' that
+# is always built. There is some special handling for the clean mode, since
+# SCons relies on actual build products for its clean processing and will not
+# run Alias() actions during clean unless they actually produce something.
+
+
+def BuildExamples(env, target, source):
+ '''Build the examples.
+
+ This runs the build command in the 'examples' directory.
+
+ Args:
+ env: The construction Environment() that is building the examples.
+ target: The target that triggered this build. Not used.
+ source: The sources used for this build. Not used.
+ '''
+ os_env = os.environ.copy()
+ os_env['NACL_TARGET_PLATFORM'] = '.'
+ subprocess.check_call(SconsBuildCommand(env),
+ cwd='examples',
+ env=os_env,
+ shell=True)
+
+
+def CleanExamples(env, target, source):
+ '''Clean the examples.
+
+ This runs the clean command in the 'examples' directory.
+
+ Args:
+ env: The construction Environment() that is building the examples.
+ '''
+ os_env = os.environ.copy()
+ os_env['NACL_TARGET_PLATFORM'] = '.'
+ subprocess.check_call('%s --clean' % SconsBuildCommand(env),
+ cwd='examples',
+ env=os_env,
+ shell=True)
+
+
+examples_builder = build_env.Alias('examples', [toolchain_node], BuildExamples)
+build_env.AlwaysBuild(examples_builder)
+build_env.AddCleanAction(['examples'], CleanExamples, ['bot'],
+ examples_builder)
+
+
+def DependsOnExamples(env, dependency):
+ env.Depends(dependency, examples_builder)
+
+build_env.AddMethod(DependsOnExamples)
+
+
+# ----------------------------------------------------------------------------
+# Add a builder for experimental projects. This adds an Alias() node named
+# 'experimental' that is always built. There is some special handling for the
+# clean mode, since SCons relies on actual build products for its clean
+# processing and will not run Alias() actions during clean unless they actually
+# produce something.
+
+
+def BuildExperimental(env, target, source):
+ '''Build the experimental projects.
+
+ This runs the build command in the 'experimental' directory.
+
+ Args:
+ env: The construction Environment() that is building the experimental
+ projects.
+ target: The target that triggered this build. Not used.
+ source: The sources used for this build. Not used.
+ '''
+ subprocess.check_call(SconsBuildCommand(env),
+ cwd='experimental',
+ shell=True)
+
+
+def CleanExperimental(env, target, source):
+ '''Clean the experimental projects.
+
+ This runs the clean command in the 'experimental' directory.
+
+ Args:
+ env: The construction Environment() that is building the experimental
+ projects.
+ '''
+ subprocess.check_call('%s --clean' % SconsBuildCommand(env),
+ cwd='experimental',
+ shell=True)
+
+
+experimental_builder = build_env.Alias('experimental', [toolchain_node],
+ BuildExperimental)
+build_env.AlwaysBuild(experimental_builder)
+build_env.AddCleanAction(['experimental'], CleanExperimental, ['bot'],
+ experimental_builder)
+
+# ----------------------------------------------------------------------------
+# Add helper functions that build/clean a test by invoking scons under the
+# test directory (|cwd|, when specified). These functions are meant to be
+# called from corresponding project-specific 'Build<project>Test' and
+# 'Clean<project>Test' functions in the local test.scons scripts. Note that the
+# CleanNaClTest does not require a |cwd| because its cwd is always '.'
+
+
+def BuildNaClTest(env, cwd):
+ '''Build the test.
+
+ This runs the build command in the test directory from which it is called.
+
+ Args:
+ env: The construction Environment() that is building the test.
+ cwd: The directory under which the test's build.scons rests.
+ '''
+ subprocess.check_call('%s stage' % SconsBuildCommand(env),
+ cwd=cwd,
+ shell=True)
+
+build_env.AddMethod(BuildNaClTest)
+
+
+def CleanNaClTest(env):
+ '''Clean the test.
+
+ This runs the clean command in the test directory from which it is called.
+
+ Args:
+ env: The construction Environment() that is building the test.
+ cwd: The directory under which the test's build.scons rests.
+ '''
+ subprocess.check_call('%s stage --clean' % SconsBuildCommand(env),
+ shell=True)
+ # The step above still leaves behind two empty 'opt' directories, so a second
+ # cleaning pass is necessary.
+ subprocess.check_call('%s --clean' % SconsBuildCommand(env),
+ shell=True)
+
+build_env.AddMethod(CleanNaClTest)
+
+# ----------------------------------------------------------------------------
+# Enable PPAPIBrowserTester() functionality using nacltest.js
+# NOTE: The three main functions in this section: PPAPIBrowserTester(),
+# CommandTest(), and AutoDepsCommand() are 'LITE' versions of their counterparts
+# provided by Native Client @ third_party/native_client/native_client/SConstruct
+
+
+def SetupBrowserEnv(env):
+ '''Set up the environment for running the browser.
+
+ This copies environment parameters provided by the OS in order to run the
+ browser reliably.
+
+ Args:
+ env: The construction Environment() that runs the browser.
+ '''
+ EXTRA_ENV = ['XAUTHORITY', 'HOME', 'DISPLAY', 'SSH_TTY', 'KRB5CCNAME']
+ for var_name in EXTRA_ENV:
+ if var_name in os.environ:
+ env['ENV'][var_name] = os.environ[var_name]
+
+ env.Append(
+ PYTHONPATH = [
+ build_utils.JoinPathToNaClRepo(
+ 'third_party', 'pylib',
+ root_dir=os.getenv('NACL_SDK_ROOT')),
+ build_utils.JoinPathToNaClRepo(
+ 'tools', 'valgrind',
+ root_dir=os.getenv('NACL_SDK_ROOT')),
+ ]
+ )
+
+
+def PPAPIBrowserTester(env,
+ target,
+ url,
+ files,
+ timeout=20):
+ '''The main test wrapper for browser integration tests.
+
+ This constructs the command that invokes the browser_tester.py script on an
+ existing Chrome binary (to be downloaded if necessary).
+
+ Args:
+ env: The construction Environment() that runs the browser.
+ target: The output file to which the output of the test is to be written.
+ url: The test web page.
+ files: The files necessary for the web page to be served.
+ timeout: How long to wait for a response before concluding failure.
+
+ Returns: A command node that executes the browser test.
+ '''
+
+ env = env.Clone()
+ SetupBrowserEnv(env)
+
+ python_tester_script = build_utils.JoinPathToNaClRepo(
+ 'native_client', 'tools', 'browser_tester', 'browser_tester.py',
+ root_dir=env['ROOT_DIR'])
+
+ # Check if browser GUI needs to be suppressed (possible only in Linux)
+ headless_prefix = []
+ if not env['SHOW_BROWSER'] and env['IS_LINUX']:
+ headless_prefix = ['xvfb-run', '--auto-servernum']
+
+ command = headless_prefix + [
+ '${PYTHON}', python_tester_script,
+ '--browser_path', env.GetChromeBinary(),
+ '--url', url,
+ # Fail if there is no response for X seconds.
+ '--timeout', str(timeout)]
+
+ for dep_file in files:
+ command.extend(['--file', dep_file])
+
+ cmd = env.CommandTest(target,
+ command,
+ # Set to 'huge' so that the browser tester's timeout
+ # takes precedence over the default of the test suite.
+ size='huge',
+ capture_output=False)
+ env.DependsOnChrome(cmd)
+
+ return cmd
+
+build_env.AddMethod(PPAPIBrowserTester)
+
+
+def CommandTest(env,
+ name,
+ command,
+ size='small',
+ capture_output=True):
+ '''The wrapper for testing execution of a command and logging details.
+
+ This constructs the command that invokes the command_tester.py script on a
+ given command.
+
+ Args:
+ env: The construction Environment() that runs the command.
+ name: The output file to which the output of the tester is to be written.
+ command: The command to be tested.
+ size: This dictates certain timeout thresholds.
+ capture_output: This specifies whether the command's output needs to be
+ captured for further processing. When this option is False,
+ stdout and stderr will be streamed out. For more info, see
+ <NACL_REPO>/native_client/tools/command_tester.py
+
+ Returns: A command node that executes the command test.
+ '''
+ TEST_TIME_THRESHOLD = {
+ 'small': 2,
+ 'medium': 10,
+ 'large': 60,
+ 'huge': 1800,
+ }
+
+ if not name.endswith('out') or name.startswith('$'):
+ raise Exception('ERROR: bad test filename for test output %r' % name)
+
+ arch_string = env.ChromeArchitectureSpec();
+ if env['IS_LINUX']:
+ platform_string = 'linux'
+ elif env['IS_MAC']:
+ platform_string = 'mac'
+ elif env['IS_WINDOWS']:
+ platform_string = 'windows'
+
+ name = '${TARGET_ROOT}/test_results/' + name
+ max_time = TEST_TIME_THRESHOLD[size]
+
+ script_flags = ['--name', name,
+ '--report', name,
+ '--time_warning', str(max_time),
+ '--time_error', str(10 * max_time),
+ '--perf_env_description', platform_string + '_' + arch_string,
+ '--arch', 'x86',
+ '--subarch', arch_string[-2:],
+ ]
+ if not capture_output:
+ script_flags.extend(['--capture_output', '0'])
+
+ test_script = build_utils.JoinPathToNaClRepo(
+ 'native_client', 'tools', 'command_tester.py',
+ root_dir=env['ROOT_DIR'])
+ command = ['${PYTHON}', test_script] + script_flags + command
+ return AutoDepsCommand(env, name, command)
+
+build_env.AddMethod(CommandTest)
+
+
+def AutoDepsCommand(env, name, command):
+ """AutoDepsCommand() takes a command as an array of arguments. Each
+ argument may either be:
+
+ * a string, or
+ * a Scons file object, e.g. one created with env.File() or as the
+ result of another build target.
+
+ In the second case, the file is automatically declared as a
+ dependency of this command.
+
+ Args:
+ env: The construction Environment() that runs the command.
+ name: The target file to which the output is to be written.
+ command: The command to be executed.
+
+ Returns: A command node in the standard SCons format.
+ """
+ deps = []
+ for index, arg in enumerate(command):
+ if not isinstance(arg, str):
+ if len(Flatten(arg)) != 1:
+ # Do not allow this, because it would cause "deps" to get out
+ # of sync with the indexes in "command".
+ # See http://code.google.com/p/nativeclient/issues/detail?id=1086
+ raise AssertionError('Argument to AutoDepsCommand() actually contains '
+ 'multiple (or zero) arguments: %r' % arg)
+ command[index] = '${SOURCES[%d].abspath}' % len(deps)
+ deps.append(arg)
+
+ return env.Command(name, deps, ' '.join(command))
+
+build_env.AddMethod(AutoDepsCommand)
+
+
+def BuildVSSolution(env, target_name, solution, project_config=None):
+ """BuildVSSolution() Builds a Visual Studio solution.
+
+ Args:
+ env: The construction Environment() that runs the command.
+ target_name: The name of the target. Build output will be written to
+ [target_name]_build_output.txt.
+ solution: The solution to build.
+ project_config: A valid project configuration string to pass into devenv.
+ This provides support for building specific configurations, i.e.
+ 'Debug|Win32', 'Debug|x64', 'Release|Win32', 'Release|x64'. Note that the
+ string must be in quotes to work. devenv will default to Win32 if this
+ is not provided.
+
+ Returns the Command() used to build the target, so other targets can be made
+ to depend on it.
+ """
+ vs_build_action = ['vcvarsall', '&&', 'devenv', '${SOURCE}', '/build']
+ if project_config:
+ vs_build_action.extend([project_config])
+
+ build_command = env.Command(target='%s_build_output.txt' % target_name,
+ source=solution,
+ action=' '.join(vs_build_action))
+ env.AddNodeAliases(build_command, ['bot'], target_name)
+ return build_command
+
+build_env.AddMethod(BuildVSSolution)
+
+
+def CleanVSSolution(env, target_name, solution_dir):
+ """CleanVSSolution() Cleans up a Visual Studio solution's build results.
+
+ The clean target created by this function is added to the 'bot' target as
+ well as the target specified. The function will clean any build artifacts
+ that could possibly be generated under the solution directory.
+
+ Args:
+ env: The construction Environment() that runs the command.
+ target_name: The name of the target which builds whatever should be cleaned
+ up.
+ solution_dir: The directory under which VS build artifacts are to be
+ expected. This function will look for Debug, Release, and x64 build
+ targets.
+ """
+ clean_targets = [os.path.join(solution_dir, 'Debug'),
+ os.path.join(solution_dir, 'Release'),
+ os.path.join(solution_dir, 'x64')]
+
+ for target in clean_targets:
+ clean_action = ['rmdir', '/Q', '/S', target]
+ env.AddCleanAction([target],
+ Action(' '.join(clean_action)),
+ ['bot'],
+ target_name)
+
+build_env.AddMethod(CleanVSSolution)
+
+
+def TestVSSolution(env, target_name, test_container, type, size, build_cmd):
+ """Defines a set of tests to be added to the scons test set.
+
+ This function adds a test solution generated by Visual Studio. It can either
+ run mstest or, for natively compiled solutions, it can run an executable.
+
+ Args:
+ env: The construction Environment() that runs the command.
+ target_name: The name of the target which resulted in the test container.
+ A name that clearly marks the target as a test is recommended here.
+ test_container: The fully qualified path to the dll or exe that contains
+ the tests.
+ type: The type test package to expect as a string. This can be 'dll' or
+ 'exe'.
+ size: Which test harness to add the tests to; small, medium, or large
+ build_cmd: The command which builds the target being tested.
+ """
+ test_action = test_container
+ if type is 'dll':
+ test_action = ' '.join(['vcvarsall',
+ '&&',
+ 'mstest',
+ '/testcontainer:%s' % test_container,
+ '/resultsfile:${TARGET}'])
+
+ # Can't use the test container as SOURCE because it is generated indirectly
+ # and using it as source would declare an explicit dependency on a target,
+ # generated by scons. Files that exist in the environment can be used in that
+ # way, but only if they exist at the time when scons starts to run, or if
+ # the are explicit Command targets. As a result, source is empty.
+ test_command = env.Command(
+ target='%s_test_results.trx' % target_name,
+ source='',
+ action=test_action)
+
+ env.Depends(test_command, build_cmd)
+ env.AddNodeToTestSuite(test_command,
+ ['bot'],
+ target_name,
+ size)
+ return test_command
+
+build_env.AddMethod(TestVSSolution)
+
+
+# ----------------------------------------------------------------------------
+BuildComponents(environment_list)
+
+# Require specifying an explicit target only when not cleaning
+if not GetOption('clean'):
+ Default(None)