summaryrefslogtreecommitdiff
path: root/lib/project_sdk.py
blob: e547a5fa66eae49dc06ae58c28cf9c1fdd36283f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Common utilities for working with Project SDK."""

from __future__ import print_function

import os
import re
import stat

from chromite.cbuildbot import constants
from chromite.lib import cros_build_lib
from chromite.lib import cros_logging as logging
from chromite.lib import osutils
from chromite.lib import workspace_lib


def FindRepoRoot(sdk_dir=None):
  """Locate the SDK root directly by looking for .repo dir.

  This is very similar to constants.SOURCE_ROOT, except that it can operate
  against repo checkouts outside our current code base.

  CAUTION! Using SDKs from directories other than the default is likely to
  break assumptions that our tools are built upon.  As a rule of thumb, do not
  expose this argument externally unless you know what you're doing.

  Args:
    sdk_dir: Path of the SDK, or any dir inside it. None defaults to
      constants.SOURCE_ROOT.

  Returns:
    Root dir of SDK, or None.
  """
  if sdk_dir is None:
    return constants.SOURCE_ROOT

  # Make sure we're looking at an actual directory.
  if not os.path.isdir(sdk_dir):
    return None

  # Find the .repo directory and return the path leading up to it, if found.
  repo_dir = osutils.FindInPathParents('.repo', os.path.abspath(sdk_dir),
                                       test_func=os.path.isdir)
  return os.path.dirname(repo_dir) if repo_dir else None


def VersionFile(sdk_dir):
  return os.path.join(sdk_dir, 'SDK_VERSION')


def FindVersion(sdk_dir=None):
  """Find the version of a given SDK.

  If the SDK was fetched by any means other than "brillo sdk" then it will
  always appear to be 'non-official', even if an official manifest was used.

  Args:
    sdk_dir: path to the SDK, or any of its sub directories.

  Returns:
    The version of your SDK as a string. '6500.0.0'
    None if the directory doesn't appear to be an SDK.
  """
  sdk_root = FindRepoRoot(sdk_dir)
  if sdk_root is None:
    return None

  v_file = VersionFile(sdk_root)
  return osutils.ReadFile(v_file) if os.path.exists(v_file) else None


def _GetExecutableVersion(cmd, version_arg='--version'):
  """Gets an executable version string using |version_flag|.

  Args:
    cmd: Executable to check (for example, '/bin/bash').
    version_arg: Argument to get |cmd| to print its version.

  Returns:
    Output string or None if the program doesn't exist or gave a
    non-zero exit code.
  """
  try:
    return cros_build_lib.RunCommand(
        [cmd, version_arg], print_cmd=False, capture_output=True).output
  except cros_build_lib.RunCommandError:
    return None


def VerifyEnvironment(workspace_path=None):
  """Verify the environment we are installed to.

  Disk permissions are only verified if a workspace path is provided.

  Args:
    workspace_path: Root directory of the workspace or None.

  Returns:
    boolean: True if the environment looks friendly.
  """
  result = True

  # Verify Python:
  #   We assume the python environment is acceptable, because we got here.
  #   However, we can add imports here to check for any required external
  #   packages.

  # Verify executables that just need to exist.
  for cmd in ('/bin/bash', 'curl'):
    if _GetExecutableVersion(cmd) is None:
      logging.error('%s is required to use the SDK.', cmd)
      result = False

  # Verify Git version.
  git_requirement_message = 'git 1.8 or greater is required to use the SDK.'
  git_version = _GetExecutableVersion('git')
  if git_version is None:
    logging.error(git_requirement_message)
    result = False

  # Example version string: 'git version 2.2.0.rc0.207.ga3a616c'.
  m = re.match(r'git version (\d+)\.(\d+)', git_version)
  if not m:
    logging.error(git_requirement_message)
    logging.error("git version not recognized from: '%s'.", git_version)
    result = False
  else:
    gv_int_list = [int(d) for d in m.groups()] # Something like [2, 3]
    if gv_int_list < [1, 8]:
      logging.error(git_requirement_message)
      logging.error("Current version: '%s'.", git_version)
      result = False

  # If a workspace path is provided, validate chroot requirements.
  if workspace_path:
    chroot_dir = workspace_lib.ChrootPath(workspace_path)

    # Create a file with the suid bit set.
    suid_file = os.path.join(chroot_dir, 'suid_test')
    try:
      # Create a file with the SUID set for the owner.
      osutils.Touch(suid_file, makedirs=True, mode=stat.S_ISUID)

      # See if the SUID bit will be respected, or ignored.
      st = os.statvfs(suid_file)

      # The os.ST_NOSUID constant wasn't added until python-3.2.
      if st.f_flag & 0x2:
        logging.error(
            'Your current chroot directory (%s) does not support the SUID bit,'
            ' which is required. You can move the chroot to a new location'
            ' using "brillo chroot --move <new_dir>"', chroot_dir)
        result = False
    finally:
      osutils.SafeUnlink(suid_file)

  return result