1# Copyright 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4"""Module to hold the Target plugin.""" 5 6import operator 7import re 8 9import cr 10import cr.base.context 11 12DEFAULT = cr.Config.From( 13 CR_DEFAULT_TARGET='chrome', 14) 15 16 17class Target(cr.base.context.Context, cr.AutoExport): 18 """Base class for implementing cr targets. 19 20 A target is something that can be built and run. 21 """ 22 23 # The default base priority 24 PRIORITY = 0 25 # The default pattern used to try to detect whether a target is a test and 26 # should use the test runner. 27 TEST_PATTERN = re.compile('tests?$') 28 # The special "test type" that means it's not a test. 29 NOT_A_TEST = 'no' 30 # The default choice for the type of test when it can't be determined. 31 NORMAL_TEST = 'gtest' 32 # TODO(iancottrell): support the other test types 33 TEST_TYPES = [NOT_A_TEST, NORMAL_TEST] 34 35 def __init__(self, target_name): 36 super(Target, self).__init__(target_name) 37 test_type = None 38 if self.TEST_PATTERN.search(target_name): 39 test_type = self.NORMAL_TEST 40 config = cr.Config('DEFAULTS').From( 41 CR_TARGET=target_name, 42 CR_TARGET_NAME='{CR_TARGET}', 43 CR_BUILD_TARGET=cr.Config.Optional( 44 '{CR_TARGET}{CR_TARGET_SUFFIX}', '{CR_TARGET}'), 45 CR_RUN_ARGUMENTS='', 46 CR_TEST_TYPE=test_type, 47 ) 48 self._data = cr.context.data 49 self.AddChildren(config, cr.context) 50 if hasattr(self, 'CONFIG'): 51 self.AddChild(self.CONFIG) 52 if not self.valid: 53 self.Set(CR_TARGET_SUFFIX='') 54 self.test_type = self.Find('CR_TEST_TYPE') 55 self.target_name = self.Find('CR_TARGET_NAME') 56 57 @property 58 def build_target(self): 59 return self.Get('CR_BUILD_TARGET') 60 61 @property 62 def valid(self): 63 return cr.Builder.IsTarget(self.build_target) 64 65 @property 66 def is_test(self): 67 return self.test_type and self.test_type != self.NOT_A_TEST 68 69 @classmethod 70 def AddArguments(cls, command, parser, allow_multiple=False): 71 nargs = '?' 72 help_string = 'The target to {0}' 73 if allow_multiple: 74 nargs = '*' 75 help_string = 'The target(s) to {0}' 76 parser.add_argument( 77 '_targets', metavar='target', 78 help=help_string.format(command.name), 79 nargs=nargs 80 ) 81 82 @classmethod 83 def AllTargets(cls): 84 yield cls 85 for child in cls.__subclasses__(): 86 for t in child.AllTargets(): 87 yield t 88 89 @classmethod 90 def CreateTarget(cls, target_name): 91 """Attempts to build a target by name. 92 93 This searches the set of installed targets in priority order to see if any 94 of them are willing to handle the supplied name. 95 If a target cannot be found, the program will be aborted. 96 Args: 97 target_name: The name of the target we are searching for. 98 Returns: 99 The target that matched. 100 """ 101 target_clses = sorted( 102 cls.AllTargets(), 103 key=operator.attrgetter('PRIORITY'), 104 reverse=True 105 ) 106 for handler in target_clses: 107 target = handler.Build(target_name) 108 if target: 109 if not target.valid: 110 print 'Invalid target {0} as {1}'.format( 111 target_name, target.build_target) 112 guesses = cr.Builder.GuessTargets(target_name) 113 if guesses: 114 print 'Did you mean {0}?'.format( 115 ', '.join(guesses[:-1]) + ' or ' + guesses[-1] 116 if len(guesses) > 1 else guesses[0]) 117 exit(1) 118 return target 119 print 'Unknown target {0}'.format(target_name) 120 exit(1) 121 122 @classmethod 123 def GetTargets(cls): 124 target_names = getattr(cr.context.args, '_targets', None) 125 if not target_names: 126 target_names = [cr.context.Get('CR_DEFAULT_TARGET')] 127 elif hasattr(target_names, 'swapcase'): 128 # deal with the single target case 129 target_names = [target_names] 130 return [cls.CreateTarget(target_name) 131 for target_name in target_names] 132 133 @classmethod 134 def Build(cls, target_name): 135 return cls(target_name) 136 137 138class NamedTarget(Target): 139 """A base class for explicit named targets. 140 141 Only matches a target if the name is an exact match. 142 Up it's priority to come ahead of general purpose rule matches. 143 """ 144 NAME = None 145 PRIORITY = Target.PRIORITY + 1 146 147 @classmethod 148 def Build(cls, target_name): 149 try: 150 if target_name == cls.NAME: 151 return cls(target_name) 152 except AttributeError: 153 pass 154 return None 155