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