1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved. 2f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 3f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# found in the LICENSE file. 4f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)"""Module for build host support.""" 6f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 7f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)import os 85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import pipes 95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import signal 10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)import subprocess 11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)import cr 13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# Controls what verbosity level turns on command trail logging 15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)_TRAIL_VERBOSITY = 2 16f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 17010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)def PrintTrail(trail): 18010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) print 'Command expanded the following variables:' 19010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) for key, value in trail: 20010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) if value == None: 21010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) value = '' 22010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) print ' ', key, '=', value 23010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class Host(cr.Plugin, cr.Plugin.Type): 26f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """Base class for implementing cr hosts. 27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 28f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) The host is the main access point to services provided by the machine cr 29f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) is running on. It exposes information about the machine, and runs external 30f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) commands on behalf of the actions. 31f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """ 32f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 33f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def __init__(self): 34f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) super(Host, self).__init__() 35f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 36f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def Matches(self): 37f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """Detects whether this is the correct host implementation. 38f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 39f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) This method is overridden by the concrete implementations. 40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Returns: 41f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) true if the plugin matches the machine it is running on. 42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """ 43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return False 44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @classmethod 46effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def Select(cls): 47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for host in cls.Plugins(): 48f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if host.Matches(): 49f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return host 50f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 51effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def _Execute(self, command, 525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) shell=False, capture=False, silent=False, 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ignore_dry_run=False, return_status=False, 545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ignore_interrupt_signal=False): 55f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """This is the only method that launches external programs. 56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 57f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) It is a thin wrapper around subprocess.Popen that handles cr specific 58effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch issues. The command is expanded in the active context so that variables 59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) are substituted. 60f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Args: 61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) command: the command to run. 62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) shell: whether to run the command using the shell. 63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) capture: controls wether the output of the command is captured. 64f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) ignore_dry_run: Normally, if the context is in dry run mode the command is 65f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) printed but not executed. This flag overrides that behaviour, causing 66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) the command to be run anyway. 67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return_status: switches the function to returning the status code rather 68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) the output. 695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ignore_interrupt_signal: Ignore the interrupt signal (i.e., Ctrl-C) while 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) the command is running. Useful for letting interactive programs manage 715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) Ctrl-C by themselves. 72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Returns: 73f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) the status if return_status is true, or the output if capture is true, 74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) otherwise nothing. 75f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """ 76effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch with cr.context.Trace(): 77effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch command = [cr.context.Substitute(arg) for arg in command if arg] 78effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch trail = cr.context.trail 79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if not command: 80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) print 'Empty command passed to execute' 81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) exit(1) 82effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if cr.context.verbose: 83f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) print ' '.join(command) 84effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if cr.context.verbose >= _TRAIL_VERBOSITY: 85010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) PrintTrail(trail) 86effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if ignore_dry_run or not cr.context.dry_run: 87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) out = None 88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if capture: 89f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) out = subprocess.PIPE 905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) elif silent: 915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) out = open(os.devnull, "w") 92f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) try: 93f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) p = subprocess.Popen( 94f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) command, shell=shell, 95effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch env={k: str(v) for k, v in cr.context.exported.items()}, 96f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) stdout=out) 97f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) except OSError: 98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) print 'Failed to exec', command 99f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) # Don't log the trail if we already have 100effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if cr.context.verbose < _TRAIL_VERBOSITY: 101010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) PrintTrail(trail) 102f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) exit(1) 1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) try: 1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if ignore_interrupt_signal: 1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) signal.signal(signal.SIGINT, signal.SIG_IGN) 1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) output, _ = p.communicate() 1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) finally: 1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if ignore_interrupt_signal: 1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) signal.signal(signal.SIGINT, signal.SIG_DFL) 1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if silent: 1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) out.close() 112f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if return_status: 113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return p.returncode 114f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if p.returncode != 0: 115f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) print 'Error {0} executing command {1}'.format(p.returncode, command) 116f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) exit(p.returncode) 117f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return output or '' 118f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return '' 119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 120f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @cr.Plugin.activemethod 121effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def Shell(self, *command): 1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) command = ' '.join([pipes.quote(arg) for arg in command]) 123effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return self._Execute([command], shell=True, ignore_interrupt_signal=True) 124f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 125f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @cr.Plugin.activemethod 126effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def Execute(self, *command): 127effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return self._Execute(command, shell=False) 128f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 129f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @cr.Plugin.activemethod 130effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def ExecuteSilently(self, *command): 131effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return self._Execute(command, shell=False, silent=True) 1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @cr.Plugin.activemethod 134effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def CaptureShell(self, *command): 135effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return self._Execute(command, 136f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) shell=True, capture=True, ignore_dry_run=True) 137f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 138f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @cr.Plugin.activemethod 139effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def Capture(self, *command): 140effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return self._Execute(command, capture=True, ignore_dry_run=True) 141f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 142f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @cr.Plugin.activemethod 143effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def ExecuteStatus(self, *command): 144effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return self._Execute(command, 145f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) ignore_dry_run=True, return_status=True) 146f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) @cr.Plugin.activemethod 1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) def YesNo(self, question, default=True): 1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) """Ask the user a yes no question 1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) This blocks until the user responds. 1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) Args: 1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) question: The question string to show the user 1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) default: True if the default response is Yes 1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) Returns: 1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) True if the response was yes. 1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) """ 1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) options = 'Y/n' if default else 'y/N' 1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) result = raw_input(question + ' [' + options + '] ').lower() 1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if result == '': 1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return default 1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return result in ['y', 'yes'] 1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 164f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @classmethod 165effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def SearchPath(cls, name, paths=[]): 166f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """Searches the PATH for an executable. 167f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 168f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Args: 169f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) name: the name of the binary to search for. 170f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Returns: 171f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) the set of executables found, or an empty list if none. 172f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """ 173f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) result = [] 174f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) extensions = [''] 175f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) extensions.extend(os.environ.get('PATHEXT', '').split(os.pathsep)) 176effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch paths = [cr.context.Substitute(path) for path in paths if path] 177effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch paths = paths + os.environ.get('PATH', '').split(os.pathsep) 178effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch for path in paths: 179f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) partial = os.path.join(path, name) 180f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for extension in extensions: 181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) filename = partial + extension 182f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if os.path.exists(filename) and filename not in result: 183f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) result.append(filename) 184f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return result 185