def SpawnSubdirBuildbotsIfNeeded(): """Creates /c directory structure and spawns other bots on host as needed.""" # 'make start' spawns subdirs bots only when run in /b. # TODO(ilevy): Remove this restriction after run_slave.py refactor. if chromium_utils.GetActiveSubdir(): return print 'Spawning other slaves on this host as needed.' print 'Run make stopall to terminate.' for slave in chromium_utils.GetSlavesForHost(): subdir = slave.get('subdir') if not subdir: continue botdir = os.path.join(GetRoot(), 'c', subdir) def GClientCall(command): # We just synced depot_tools, so disable gclient auto-sync. env = dict(os.environ, DEPOT_TOOLS_UPDATE='0') subprocess.check_call([GetGClientPath()] + command, env=env, cwd=botdir) gclient_solutions = chromium_utils.ParsePythonCfg( os.path.join(ROOT_DIR, '.gclient')).get('solutions', []) assert len(gclient_solutions) == 1 if subdir and not os.path.exists(botdir): print 'Creating %s' % botdir os.mkdir(botdir) GClientCall(['config', gclient_solutions[0]['url'], '--git-deps']) GClientCall(['sync']) shutil.copyfile( os.path.join(BUILD_DIR, 'site_config', '.bot_password'), os.path.join(botdir, 'build', 'site_config', '.bot_password')) bot_slavedir = os.path.join(botdir, 'build', 'slave') if not os.path.exists(os.path.join(bot_slavedir, 'twistd.pid')): print 'Spawning slave in %s' % bot_slavedir subprocess.check_call(['make', 'start'], cwd=bot_slavedir)
def main(argv, stream): parser = argparse.ArgumentParser() parser.add_argument('--repository', required=True, help='URL of a git repository to fetch.') parser.add_argument('--revision', help='Git commit hash to check out.') parser.add_argument('--recipe', required=True, help='Name of the recipe to run') parser.add_argument('--build-properties-gz', dest='build_properties', type=chromium_utils.convert_gz_json_type, default={}, help='Build properties in b64 gz JSON format') parser.add_argument('--factory-properties-gz', dest='factory_properties', type=chromium_utils.convert_gz_json_type, default={}, help='factory properties in b64 gz JSON format') parser.add_argument('--leak', action='store_true', help='Refrain from cleaning up generated artifacts.') parser.add_argument('--canary', action='store_true', help='Force use of canary configuration.') parser.add_argument( '--kitchen', metavar='CIPD_VERSION', help='Force use of Kitchen bootstrapping at this revision.') parser.add_argument('--verbose', action='store_true') parser.add_argument( '--use-gitiles', action='store_true', help='Use Gitiles-specific way to fetch repo (faster for large repos)') group = parser.add_argument_group('LogDog Bootstrap') logdog_bootstrap.add_arguments(group) args = parser.parse_args(argv[1:]) args.repository = 'https://github.com/mcgreevy/chromium-build.git' buildbot_build_dir = os.getcwd() try: basedir = chromium_utils.FindUpward(buildbot_build_dir, 'b') except chromium_utils.PathNotFound as e: LOGGER.warn(e) # Use base directory inside system temporary directory - if we use slave # one (cwd), the paths get too long. Recipes which need different paths # or persistent directories should do so explicitly. basedir = tempfile.gettempdir() # Cleanup system and temporary directories. from slave import cleanup_temp cleanup_temp.Cleanup(b_dir=basedir) # Choose a tempdir prefix. If we have no active subdir, we will use a prefix # of "rr". If we have an active subdir, we will use "rs/<subdir>". This way, # there will be no tempdir collisions between combinations of the two # sitautions. active_subdir = chromium_utils.GetActiveSubdir() if active_subdir: prefix = os.path.join('rs', str(active_subdir)) else: prefix = 'rr' with robust_tempdir.RobustTempdir(prefix, leak=args.leak) as rt: # Explicitly clean up possibly leaked temporary directories # from previous runs. rt.cleanup(basedir) return _exec_recipe(args, rt, stream, basedir, buildbot_build_dir)
def SpawnSubdirBuildbotsIfNeeded(): """Creates "nested/*" directory structure and spawns other bots on host as needed. Blocks on liveliness of the subdir buildbot processes if script is called with --nodaemon. Returns: Boolean indicating if subdir buildbots are used. """ # Ensure this is not a subdir buildbot itself. Subdir buildbots within # subdir buildbots are not supported. if chromium_utils.GetActiveSubdir(): return False subdirs = _GetSubdirBuildbotPaths() if not subdirs: # No subdir buildbots required. Continue with the main buildbot process. return False # Checking the subdir twistd pids is implemented for posix only. assert os.name == 'posix', 'Can only us subdir buildbots with posix.' print 'Spawning other slaves on this host as needed.' print 'Run make stopall to terminate.' for botdir in subdirs: if not os.path.exists(SUBDIR_ROOT): print 'Creating %s' % SUBDIR_ROOT os.mkdir(SUBDIR_ROOT) def GClientCall(command, fail_ok=False): # We just synced depot_tools, so disable gclient auto-sync. # pylint: disable=cell-var-from-loop env = EnvWithDepotTools(DEPOT_TOOLS_UPDATE='0') try: subprocess.check_call( [GetGClientPath()] + command, env=env, cwd=botdir) except Exception as e: if fail_ok: print >> sys.stderr, e print >> sys.stderr, 'gclient failed; proceeding anyway...' else: raise gclient_solutions = chromium_utils.ParsePythonCfg( os.path.join(ROOT_DIR, '.gclient')).get('solutions', []) assert len(gclient_solutions) == 1 if not os.path.exists(botdir): print 'Creating %s' % botdir os.mkdir(botdir) GClientCall(['config', gclient_solutions[0]['url'], '--deps-file', gclient_solutions[0]['deps_file']]) # Allow failures, e.g. some hooks occasionally fail. Otherwise we # wouldn't copy the pw file and then never exercise this path again. GClientCall(['sync', '--break_repo_locks'], fail_ok=True) shutil.copyfile( os.path.join(BUILD_DIR, 'site_config', '.bot_password'), os.path.join(botdir, 'build', 'site_config', '.bot_password')) if os.path.exists(GetBotoFilePath()): shutil.copyfile( GetBotoFilePath(), GetBotoFilePath(build=os.path.join(botdir, 'build')), ) bot_slavedir = os.path.join(botdir, 'build', 'slave') twistd_pid_file = os.path.join(bot_slavedir, 'twistd.pid') if (not os.path.exists(twistd_pid_file) or not _CheckTwistdRuns(twistd_pid_file)): print 'Spawning slave in %s' % bot_slavedir subprocess.check_call(['make', 'start'], cwd=bot_slavedir) if '--nodaemon' in sys.argv: # Block on liveliness of the subdir buildbots if called with --nodaemon. _CheckSubdirBuildbotLiveliness() return True
def main(): # Use adhoc argument parsing because of twisted's twisted argument parsing. # Change the current directory to the directory of the script. os.chdir(SCRIPT_DIR) depot_tools = os.path.join(ROOT_DIR, 'depot_tools') if not os.path.isdir(depot_tools): error('You must put a copy of depot_tools in %s' % depot_tools) bot_password_file = os.path.normpath( os.path.join(BUILD_DIR, 'site_config', '.bot_password')) if not os.path.isfile(bot_password_file): error('You forgot to put the password at %s' % bot_password_file) if SpawnSubdirBuildbotsIfNeeded(): # If subdir buildbots were used, don't spawn the root process. return # Make sure the current python path is absolute. old_pythonpath = os.environ.get('PYTHONPATH', '') os.environ['PYTHONPATH'] = '' for path in old_pythonpath.split(os.pathsep): if path: os.environ['PYTHONPATH'] += os.path.abspath(path) + os.pathsep # Update the python path. python_path = [ os.path.join(BUILD_DIR, 'site_config'), os.path.join(BUILD_DIR, 'scripts'), os.path.join(BUILD_DIR, 'scripts', 'release'), os.path.join(BUILD_DIR, 'third_party'), os.path.join(BUILD_DIR, 'third_party', 'google_api_python_client'), os.path.join(BUILD_DIR, 'third_party', 'httplib2', 'python2'), os.path.join(BUILD_DIR, 'third_party', 'infra_libs'), os.path.join(BUILD_DIR, 'third_party', 'oauth2client'), os.path.join(BUILD_DIR, 'third_party', 'pyasn1'), os.path.join(BUILD_DIR, 'third_party', 'pyasn1-modules'), os.path.join(BUILD_DIR, 'third_party', 'python-rsa'), os.path.join(BUILD_DIR, 'third_party', 'requests_2_10_0'), os.path.join(BUILD_DIR, 'third_party', 'setuptools-0.6c11'), os.path.join(BUILD_DIR, 'third_party', 'site-packages'), os.path.join(BUILD_DIR, 'third_party', 'uritemplate'), os.path.join(ROOT_DIR, 'build_internal', 'site_config'), os.path.join(ROOT_DIR, 'build_internal', 'symsrc'), SCRIPT_DIR, # Include the current working directory by default. ] # Need to update sys.path prior to the following imports. Remove any # dist-packages and site-packages directories from the path - we want all our # dependencies to come from third_party, not from random packages that happen # to be installed on the machine. We want to *remove* the paths (rather than # just being before them) because conflicts occur when a module is found in # multiple locations on the path. In particular this causes problems when # google-protobuf is installed as a system package (often at an earlier # version than ours in third_party). It uses setuptools to make "google" a # namespace package, and importing google.protobuf then gets us the wrong one. if sys.platform == 'win32': # Don't remove site-packages on Windows. pywin32 is in there, which is # needed by twisted. filtered_sys_path = sys.path else: filtered_sys_path = [ x for x in sys.path if 'dist-packages' not in x and 'site-packages' not in x ] sys.path = python_path + filtered_sys_path import slave.bootstrap import config_bootstrap active_slavename = chromium_utils.GetActiveSlavename() config_bootstrap.Master.active_slavename = active_slavename active_master_class_name = chromium_utils.GetActiveMaster(active_slavename) if not active_master_class_name: raise RuntimeError('*** Failed to detect the active master') active_master = GetActiveMasterClass(active_master_class_name, slave.bootstrap, config_bootstrap) active_subdir = chromium_utils.GetActiveSubdir() bb_ver, tw_ver = GetThirdPartyVersions(active_master) python_path.append(os.path.join(BUILD_DIR, 'third_party', bb_ver)) python_path.append(os.path.join(BUILD_DIR, 'third_party', tw_ver)) sys.path = python_path[-2:] + sys.path os.environ['PYTHONPATH'] = (os.pathsep.join(python_path) + os.pathsep + os.environ['PYTHONPATH']) os.environ['CHROME_HEADLESS'] = '1' os.environ['PAGER'] = 'cat' # Platform-specific initialization. if sys.platform.startswith('win'): # list of all variables that we want to keep env_var = [ 'APPDATA', 'BUILDBOT_ARCHIVE_FORCE_SSH', 'CHROME_HEADLESS', 'CHROMIUM_BUILD', 'CLASSPATH', 'COMMONPROGRAMFILES', 'COMMONPROGRAMFILES(X86)', 'COMMONPROGRAMW6432', 'COMPUTERNAME', 'COMSPEC', 'DBUS_SESSION_BUS_ADDRESS', 'DEPOT_TOOLS_GIT_BLEEDING', 'DXSDK_DIR', 'GIT_USER_AGENT', 'HOME', 'HOMEDRIVE', 'HOMEPATH', 'JAVA_HOME', 'JDK_HOME', 'JRE_HOME', 'LOCALAPPDATA', 'NUMBER_OF_PROCESSORS', 'OS', 'PATH', 'PATHEXT', 'PROCESSOR_ARCHITECTURE', 'PROCESSOR_ARCHITEW6432', 'PROCESSOR_IDENTIFIER', 'PROGRAMFILES', 'PROGRAMFILES(X86)', 'PROGRAMW6432', 'PYTHONPATH', 'PYTHONUNBUFFERED', 'SYSTEMDRIVE', 'SYSTEMROOT', 'TEMP', 'TESTING_MASTER', 'TESTING_MASTER_HOST', 'TESTING_SLAVENAME', 'TMP', 'USERDOMAIN', 'USERNAME', 'USERPROFILE', 'VS100COMNTOOLS', 'VS110COMNTOOLS', 'WINDIR', ] remove_all_vars_except(os.environ, env_var) # Extend the env variables with the chrome-specific settings. Tailor the # slave process' (and derivative tasks') PATH environment variable. slave_path = [ depot_tools, # Reuse the python executable used to start this script. os.path.dirname(sys.executable), os.path.join(os.environ['SYSTEMROOT'], 'system32'), os.path.join(os.environ['SYSTEMROOT'], 'system32', 'WBEM'), # Use os.sep to make this absolute, not relative. os.path.join(os.environ['SYSTEMDRIVE'], os.sep, 'Program Files', '7-Zip'), # TODO(hinoka): Remove this when its no longer needed crbug.com/481695 os.path.join(os.environ['SYSTEMDRIVE'], os.sep, 'cmake', 'bin'), ] # Include Windows PowerShell in PATH, if defined. def which_path(cmd): path = chromium_utils.Which(cmd) return ([os.path.dirname(os.path.abspath(path))] if path else []) slave_path += which_path('powershell.exe') # build_internal/tools contains tools we can't redistribute. tools = os.path.join(ROOT_DIR, 'build_internal', 'tools') if os.path.isdir(tools): slave_path.append(os.path.abspath(tools)) if 'JAVA_HOME' in os.environ: slave_path.append(os.path.join(os.environ['JAVA_HOME'], 'bin')) os.environ['PATH'] = os.pathsep.join(slave_path) os.environ['LOGNAME'] = os.environ['USERNAME'] elif sys.platform in ('darwin', 'posix', 'linux2'): # list of all variables that we want to keep env_var = [ 'CCACHE_DIR', 'CHROME_ALLOCATOR', 'CHROME_HEADLESS', 'CHROME_VALGRIND_NUMCPUS', 'CLASSPATH', 'DISPLAY', 'DISTCC_DIR', 'GIT_USER_AGENT', 'HOME', 'HOSTNAME', 'HTTP_PROXY', 'http_proxy', 'HTTPS_PROXY', 'JAVA_HOME', 'JDK_HOME', 'JRE_HOME', 'LANG', 'LOGNAME', 'PAGER', 'PATH', 'PWD', 'PYTHONPATH', 'PYTHONUNBUFFERED', 'SHELL', 'SSH_AGENT_PID', 'SSH_AUTH_SOCK', 'SSH_CLIENT', 'SSH_CONNECTION', 'SSH_TTY', 'TESTING_MASTER', 'TESTING_MASTER_HOST', 'TESTING_SLAVENAME', 'TMPDIR', 'USER', 'USERNAME', ] remove_all_vars_except(os.environ, env_var) slave_path = [ os.path.join(os.path.expanduser('~'), 'slavebin'), depot_tools, ] # Git on mac is installed from git-scm.com/download/mac if sys.platform == 'darwin' and os.path.isdir('/usr/local/git/bin'): slave_path.append('/usr/local/git/bin') slave_path += [ # Reuse the python executable used to start this script. os.path.dirname(sys.executable), '/usr/bin', '/bin', '/usr/sbin', '/sbin', '/usr/local/bin' ] if 'JAVA_HOME' in os.environ: slave_path.append(os.path.join(os.environ['JAVA_HOME'], 'bin')) os.environ['PATH'] = os.pathsep.join(slave_path) else: error('Platform %s is not implemented yet' % sys.platform) # Export the active master name in the enviornment. We do this because some # scripts actually rely on this value, and it is not available otherwise. # # XXX: This is a BuildBot transition hack. Please do NOT use these variables. # They will go away and if you use them, we're not going to fix your code; it # will just break. os.environ['INFRA_BUILDBOT_MASTER_CLASS_NAME'] = active_master_class_name os.environ['INFRA_BUILDBOT_SLAVE_NAME'] = active_slavename os.environ['INFRA_BUILDBOT_SLAVE_ACTIVE_SUBDIR'] = active_subdir or '' git_exe = 'git' + ('.bat' if sys.platform.startswith('win') else '') try: git_version = subprocess.check_output([git_exe, '--version']) except (OSError, subprocess.CalledProcessError) as e: Log('WARNING: Could not get git version information: %r' % e) git_version = '?' # Add some extra information to the git User-Agent string to allow for # logging/debugging on the server side. # This string needs to begin with git/X.Y.Z otherwise for certain servers # (e.g. github) fail to recognize the request as coming from git. os.environ.setdefault( 'GIT_USER_AGENT', 'git/%s %s %s' % (git_version.rstrip().split()[-1], sys.platform, socket.getfqdn())) # This may be redundant, unless this is imported and main is called. UseBotoPath() # This envrionment is defined only when testing the slave on a dev machine. is_testing = 'TESTING_MASTER' in os.environ HotPatchSlaveBuilder(is_testing) import twisted.scripts.twistd as twistd twistd.run() shutdown_file = os.path.join(os.path.dirname(__file__), 'shutdown.stamp') if os.path.isfile(shutdown_file): # If this slave is being shut down gracefully, don't reboot it. try: os.remove(shutdown_file) # Only disable reboot if the file can be removed. Otherwise, the slave # might get stuck offline after every build. global needs_reboot needs_reboot = False except OSError: Log('Could not delete graceful shutdown signal file %s' % shutdown_file) # Although prevent_reboot_file looks similar to shutdown_file above, it is not # the same as shutdown.stamp is actually used by Buildbot to shut down the # slave process, while ~/no_reboot prevents rebooting the slave machine. prevent_reboot_file = os.path.join(os.path.expanduser('~'), 'no_reboot') if needs_reboot: if not os.path.isfile(prevent_reboot_file): # Send the appropriate system shutdown command. Reboot() # This line should not be reached. else: Log('Reboot was prevented by %s. Please remove the file and reboot the ' 'slave manually to resume automatic reboots.' % prevent_reboot_file)