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
5"""A module for the init command."""
6
7import os
8
9import cr
10
11# The set of variables to store in the per output configuration.
12OUT_CONFIG_VARS = [
13    'CR_VERSION',
14    cr.Platform.SELECTOR, cr.BuildType.SELECTOR, cr.Arch.SELECTOR,
15    'CR_OUT_BASE', 'CR_OUT_FULL',
16]
17
18
19class InitCommand(cr.Command):
20  """The implementation of the init command.
21
22  The init command builds or updates an output directory.
23  It then uses the Prepare and Select commands to get that directory
24  ready to use.
25  """
26
27  def __init__(self):
28    super(InitCommand, self).__init__()
29    self.requires_build_dir = False
30    self.help = 'Create and configure an output directory'
31    self.description = ("""
32        If the .cr directory is not present, build it and add
33        the specified configuration.
34        If the file already exists, update the configuration with any
35        additional settings.
36        """)
37    self._settings = []
38
39  def AddArguments(self, subparsers):
40    """Overridden from cr.Command."""
41    parser = super(InitCommand, self).AddArguments(subparsers)
42    cr.Platform.AddArguments(parser)
43    cr.BuildType.AddArguments(parser)
44    cr.Arch.AddArguments(parser)
45    cr.SelectCommand.AddPrepareArguments(parser)
46    parser.add_argument(
47        '-s', '--set', dest='_settings', metavar='settings',
48        action='append',
49        help='Configuration overrides.'
50    )
51    return parser
52
53  def EarlyArgProcessing(self):
54    base_settings = getattr(cr.context.args, '_settings', None)
55    if base_settings:
56      self._settings.extend(base_settings)
57    # Do not call super early processing, we do not want to apply
58    # the output arg...
59    out = cr.base.client.GetOutArgument()
60    if out:
61      # Output directory is fully specified
62      # We need to deduce other settings from it's name
63      base, buildtype = os.path.split(out)
64      if not (base and buildtype):
65        print 'Specified output directory must be two levels'
66        exit(1)
67      if not cr.BuildType.FindPlugin(buildtype):
68        print 'Specified build type', buildtype, 'is not valid'
69        print 'Must be one of', ','.join(p.name for p in cr.BuildType.Plugins())
70        exit(1)
71      if (cr.context.args.CR_BUILDTYPE and
72          cr.context.args.CR_BUILDTYPE != buildtype):
73        print 'If --type and --out are both specified, they must match'
74        print 'Got', cr.context.args.CR_BUILDTYPE, 'and', buildtype
75        exit(1)
76      platform = cr.context.args.CR_PLATFORM
77      if not platform:
78        # Try to guess platform based on output name
79        platforms = [p.name for p in cr.Platform.AllPlugins()]
80        matches = [p for p in platforms if p in base]
81        if len(matches) != 1:
82          print 'Platform is not set, and could not be guessed from', base
83          print 'Should be one of', ','.join(platforms)
84          if len(matches) > 1:
85            print 'Matched all of', ','.join(matches)
86          exit(1)
87        platform = matches[0]
88      cr.context.derived.Set(
89          CR_OUT_FULL=out,
90          CR_OUT_BASE=base,
91          CR_PLATFORM=platform,
92          CR_BUILDTYPE=buildtype,
93      )
94    if not 'CR_OUT_BASE' in cr.context:
95      cr.context.derived['CR_OUT_BASE'] = 'out_{CR_PLATFORM}'
96    if not 'CR_OUT_FULL' in cr.context:
97      cr.context.derived['CR_OUT_FULL'] = os.path.join(
98          '{CR_OUT_BASE}', '{CR_BUILDTYPE}')
99
100  def Run(self):
101    """Overridden from cr.Command."""
102    src_path = cr.context.Get('CR_SRC')
103    if not os.path.isdir(src_path):
104      print cr.context.Substitute('Path {CR_SRC} is not a valid client')
105      exit(1)
106
107    # Ensure we have an output directory override ready to fill in
108    # This will only be missing if we are creating a brand new output
109    # directory
110    build_package = cr.auto.build
111
112    # Collect the old version (and float convert)
113    old_version = cr.context.Find('CR_VERSION')
114    try:
115      old_version = float(old_version)
116    except (ValueError, TypeError):
117      old_version = 0.0
118    is_new = not hasattr(build_package, 'config')
119    if is_new:
120
121      class FakeModule(object):
122        OVERRIDES = cr.Config('OVERRIDES')
123
124        def __init__(self):
125          self.__name__ = 'config'
126
127      old_version = None
128      config = FakeModule()
129      setattr(build_package, 'config', config)
130      cr.plugin.ChainModuleConfigs(config)
131
132    # Force override the version
133    build_package.config.OVERRIDES.Set(CR_VERSION=cr.base.client.VERSION)
134    # Add all the variables that we always want to have
135    for name in OUT_CONFIG_VARS:
136      value = cr.context.Find(name)
137      build_package.config.OVERRIDES[name] = value
138    # Apply the settings from the command line
139    for setting in self._settings:
140      name, separator, value = setting.partition('=')
141      name = name.strip()
142      if not separator:
143        value = True
144      else:
145        value = cr.Config.ParseValue(value.strip())
146      build_package.config.OVERRIDES[name] = value
147
148    # Run all the output directory init hooks
149    for hook in cr.InitHook.Plugins():
150      hook.Run(old_version, build_package.config)
151    # Redo activations, they might have changed
152    cr.plugin.Activate()
153
154    # Write out the new configuration, and select it as the default
155    cr.base.client.WriteConfig(cr.context.Get('CR_BUILD_DIR'),
156                               build_package.config.OVERRIDES.exported)
157    # Prepare the platform in here, using the updated config
158    cr.Platform.Prepare()
159    cr.SelectCommand.Select()
160