2 # Copyright 2020 The Pigweed Authors
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 # use this file except in compliance with the License. You may obtain a copy of
8 # https://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations under
15 """Installs or updates prebuilt tools.
17 Must be tested with Python 2 and Python 3.
19 The stdout of this script is meant to be executed by the invoking shell.
22 from __future__ import print_function
35 """Parse arguments."""
37 script_root = os.path.join(os.environ['PW_ROOT'], 'pw_env_setup', 'py',
38 'pw_env_setup', 'cipd_setup')
39 git_root = subprocess.check_output(
40 ('git', 'rev-parse', '--show-toplevel'),
42 ).decode('utf-8').strip()
44 parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
47 dest='root_install_dir',
48 default=os.path.join(git_root, '.cipd'),
50 parser.add_argument('--package-file',
52 metavar='PACKAGE_FILE',
54 parser.add_argument('--cipd',
55 default=os.path.join(script_root, 'wrapper.py'))
56 parser.add_argument('--cache-dir',
57 default=os.environ.get(
59 os.path.expanduser('~/.cipd-cache-dir')))
61 return parser.parse_args(argv)
64 def check_auth(cipd, package_files):
65 """Check have access to CIPD pigweed directory."""
68 for package_file in package_files:
69 with open(package_file, 'r') as ins:
70 # This is an expensive RPC, so only check the first few entries
72 for i, entry in enumerate(json.load(ins)):
75 parts = entry['path'].split('/')
76 while '${' in parts[-1]:
78 paths.append('/'.join(parts))
81 output = subprocess.check_output([cipd, 'auth-info'],
82 stderr=subprocess.STDOUT).decode()
86 match = re.search(r'Logged in as (\S*)\.', output)
88 username = match.group(1)
90 except subprocess.CalledProcessError:
94 # Not catching CalledProcessError because 'cipd ls' seems to never
95 # return an error code unless it can't reach the CIPD server.
96 output = subprocess.check_output([cipd, 'ls', path],
97 stderr=subprocess.STDOUT).decode()
98 if 'No matching packages' not in output:
101 # 'cipd ls' only lists sub-packages but ignores any packages at the
102 # given path. 'cipd instances' will give versions of that package.
103 # 'cipd instances' does use an error code if there's no such package or
104 # that package is inaccessible.
106 subprocess.check_output([cipd, 'instances', path],
107 stderr=subprocess.STDOUT)
108 except subprocess.CalledProcessError:
109 stderr = lambda *args: print(*args, file=sys.stderr)
112 stderr('ERROR: no access to CIPD path "{}"'.format(path))
116 username_part = '({}) '.format(username)
117 stderr('Your account {}does not have access to this '
118 'path'.format(username_part))
120 stderr('Try logging in with this command:')
122 stderr(' {} auth-login'.format(cipd))
129 def write_ensure_file(package_file, ensure_file):
130 with open(package_file, 'r') as ins:
131 data = json.load(ins)
133 # TODO(pwbug/103) Remove 30 days after bug fixed.
134 if os.path.isdir(ensure_file):
135 shutil.rmtree(ensure_file)
137 with open(ensure_file, 'w') as outs:
138 outs.write('$VerifiedPlatform linux-amd64\n'
139 '$VerifiedPlatform mac-amd64\n'
140 '$ParanoidMode CheckPresence\n')
143 outs.write('@Subdir {}\n'.format(entry.get('subdir', '')))
144 outs.write('{} {}\n'.format(entry['path'],
145 ' '.join(entry['tags'])))
155 """Grab the tools listed in ensure_files."""
157 if not check_auth(cipd, package_files):
160 # TODO(mohrr) use os.makedirs(..., exist_ok=True).
161 if not os.path.isdir(root_install_dir):
162 os.makedirs(root_install_dir)
165 env_vars.prepend('PATH', root_install_dir)
166 env_vars.set('PW_CIPD_INSTALL_DIR', root_install_dir)
167 env_vars.set('CIPD_CACHE_DIR', cache_dir)
171 pw_root = env_vars.get('PW_ROOT', None)
173 pw_root = os.environ['PW_ROOT']
175 # Run cipd for each json file.
176 for package_file in package_files:
177 if os.path.splitext(package_file)[1] == '.ensure':
178 ensure_file = package_file
180 ensure_file = os.path.join(
183 os.path.splitext(package_file)[0] + '.ensure'))
184 write_ensure_file(package_file, ensure_file)
186 install_dir = os.path.join(
188 os.path.basename(os.path.splitext(package_file)[0]))
193 '-ensure-file', ensure_file,
194 '-root', install_dir,
195 '-log-level', 'warning',
196 '-cache-dir', cache_dir,
197 '-max-threads', '0', # 0 means use CPU count.
200 # TODO(pwbug/135) Use function from common utility module.
201 with tempfile.TemporaryFile(mode='w+') as temp:
202 print(*cmd, file=temp)
204 subprocess.check_call(cmd,
206 stderr=subprocess.STDOUT)
207 except subprocess.CalledProcessError:
209 sys.stderr.write(temp.read())
212 # Set environment variables so tools can later find things under, for
214 name = os.path.basename(install_dir)
217 # Some executables get installed at top-level and some get
218 # installed under 'bin'.
219 env_vars.prepend('PATH', install_dir)
220 env_vars.prepend('PATH', os.path.join(install_dir, 'bin'))
221 env_vars.set('PW_{}_CIPD_INSTALL_DIR'.format(name.upper()),
224 # Windows has its own special toolchain.
226 env_vars.prepend('PATH',
227 os.path.join(install_dir, 'mingw64', 'bin'))
232 if __name__ == '__main__':
233 update(**vars(parse()))