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