Skip to content

Commit 4e0221c

Browse files
authored
A1
1 parent 0e14212 commit 4e0221c

File tree

1 file changed

+370
-0
lines changed

1 file changed

+370
-0
lines changed

.github/workflows/release-doctor.yml

Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,375 @@
11
name: Release Doctor
22
on:
3+
#!/usr/bin/env python3
4+
5+
# Buildtools and buildtools extended installer helper script
6+
#
7+
# Copyright (C) 2017-2020 Intel Corporation
8+
#
9+
# SPDX-License-Identifier: GPL-2.0-only
10+
#
11+
# NOTE: --with-extended-buildtools is on by default
12+
#
13+
# Example usage (extended buildtools from milestone):
14+
# (1) using --url and --filename
15+
# $ install-buildtools \
16+
# --url http://downloads.yoctoproject.org/releases/yocto/milestones/yocto-3.1_M3/buildtools \
17+
# --filename x86_64-buildtools-extended-nativesdk-standalone-3.0+snapshot-20200315.sh
18+
# (2) using --base-url, --release, --installer-version and --build-date
19+
# $ install-buildtools \
20+
# --base-url http://downloads.yoctoproject.org/releases/yocto \
21+
# --release yocto-3.1_M3 \
22+
# --installer-version 3.0+snapshot
23+
# --build-date 202000315
24+
#
25+
# Example usage (standard buildtools from release):
26+
# (3) using --url and --filename
27+
# $ install-buildtools --without-extended-buildtools \
28+
# --url http://downloads.yoctoproject.org/releases/yocto/yocto-3.0.2/buildtools \
29+
# --filename x86_64-buildtools-nativesdk-standalone-3.0.2.sh
30+
# (4) using --base-url, --release and --installer-version
31+
# $ install-buildtools --without-extended-buildtools \
32+
# --base-url http://downloads.yoctoproject.org/releases/yocto \
33+
# --release yocto-3.0.2 \
34+
# --installer-version 3.0.2
35+
#
36+
37+
import argparse
38+
import logging
39+
import os
40+
import platform
41+
import re
42+
import shutil
43+
import shlex
44+
import stat
45+
import subprocess
46+
import sys
47+
import tempfile
48+
from urllib.parse import quote
49+
50+
scripts_path = os.path.dirname(os.path.realpath(__file__))
51+
lib_path = scripts_path + '/lib'
52+
sys.path = sys.path + [lib_path]
53+
import scriptutils
54+
import scriptpath
55+
56+
57+
PROGNAME = 'install-buildtools'
58+
logger = scriptutils.logger_create(PROGNAME, stream=sys.stdout)
59+
60+
DEFAULT_INSTALL_DIR = os.path.join(os.path.split(scripts_path)[0],'buildtools')
61+
DEFAULT_BASE_URL = 'https://downloads.yoctoproject.org/releases/yocto'
62+
DEFAULT_RELEASE = 'yocto-5.2.2'
63+
DEFAULT_INSTALLER_VERSION = '5.2.2'
64+
DEFAULT_BUILDDATE = '202110XX'
65+
66+
# Python version sanity check
67+
if not (sys.version_info.major == 3 and sys.version_info.minor >= 4):
68+
logger.error("This script requires Python 3.4 or greater")
69+
logger.error("You have Python %s.%s" %
70+
(sys.version_info.major, sys.version_info.minor))
71+
sys.exit(1)
72+
73+
# The following three functions are copied directly from
74+
# bitbake/lib/bb/utils.py, in order to allow this script
75+
# to run on versions of python earlier than what bitbake
76+
# supports (e.g. less than Python 3.5 for YP 3.1 release)
77+
78+
def _hasher(method, filename):
79+
import mmap
80+
81+
with open(filename, "rb") as f:
82+
try:
83+
with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
84+
for chunk in iter(lambda: mm.read(8192), b''):
85+
method.update(chunk)
86+
except ValueError:
87+
# You can't mmap() an empty file so silence this exception
88+
pass
89+
return method.hexdigest()
90+
91+
92+
def md5_file(filename):
93+
"""
94+
Return the hex string representation of the MD5 checksum of filename.
95+
"""
96+
import hashlib
97+
return _hasher(hashlib.md5(), filename)
98+
99+
def sha256_file(filename):
100+
"""
101+
Return the hex string representation of the 256-bit SHA checksum of
102+
filename.
103+
"""
104+
import hashlib
105+
return _hasher(hashlib.sha256(), filename)
106+
107+
def remove_quotes(var):
108+
"""
109+
If a variable starts and ends with double quotes, remove them.
110+
Assumption: if a variable starts with double quotes, it must also
111+
end with them.
112+
"""
113+
if var[0] == '"':
114+
var = var[1:-1]
115+
return var
116+
117+
118+
def main():
119+
global DEFAULT_INSTALL_DIR
120+
global DEFAULT_BASE_URL
121+
global DEFAULT_RELEASE
122+
global DEFAULT_INSTALLER_VERSION
123+
global DEFAULT_BUILDDATE
124+
filename = ""
125+
release = ""
126+
buildtools_url = ""
127+
install_dir = ""
128+
arch = platform.machine()
129+
130+
parser = argparse.ArgumentParser(
131+
description="Buildtools installation helper",
132+
add_help=False,
133+
formatter_class=argparse.RawTextHelpFormatter)
134+
parser.add_argument('-u', '--url',
135+
help='URL from where to fetch buildtools SDK installer, not '
136+
'including filename (optional)\n'
137+
'Requires --filename.',
138+
action='store')
139+
parser.add_argument('-f', '--filename',
140+
help='filename for the buildtools SDK installer to be installed '
141+
'(optional)\nRequires --url',
142+
action='store')
143+
parser.add_argument('-d', '--directory',
144+
default=DEFAULT_INSTALL_DIR,
145+
help='directory where buildtools SDK will be installed (optional)',
146+
action='store')
147+
parser.add_argument('--downloads-directory',
148+
help='use this directory for tarball/checksum downloads and do not erase them (default is a temporary directory which is deleted after unpacking and installing the buildtools)',
149+
action='store')
150+
parser.add_argument('-r', '--release',
151+
default=DEFAULT_RELEASE,
152+
help='Yocto Project release string for SDK which will be '
153+
'installed (optional)',
154+
action='store')
155+
parser.add_argument('-V', '--installer-version',
156+
default=DEFAULT_INSTALLER_VERSION,
157+
help='version string for the SDK to be installed (optional)',
158+
action='store')
159+
parser.add_argument('-b', '--base-url',
160+
default=DEFAULT_BASE_URL,
161+
help='base URL from which to fetch SDK (optional)', action='store')
162+
parser.add_argument('-t', '--build-date',
163+
default=DEFAULT_BUILDDATE,
164+
help='Build date of pre-release SDK (optional)', action='store')
165+
group = parser.add_mutually_exclusive_group()
166+
group.add_argument('--with-extended-buildtools', action='store_true',
167+
dest='with_extended_buildtools',
168+
default=True,
169+
help='enable extended buildtools tarball (on by default)')
170+
group.add_argument('--without-extended-buildtools', action='store_false',
171+
dest='with_extended_buildtools',
172+
help='disable extended buildtools (traditional buildtools tarball)')
173+
group.add_argument('--make-only', action='store_true',
174+
help='only install make tarball')
175+
group = parser.add_mutually_exclusive_group()
176+
group.add_argument('-c', '--check', help='enable checksum validation',
177+
default=True, action='store_true')
178+
group.add_argument('-n', '--no-check', help='disable checksum validation',
179+
dest="check", action='store_false')
180+
parser.add_argument('-D', '--debug', help='enable debug output',
181+
action='store_true')
182+
parser.add_argument('-q', '--quiet', help='print only errors',
183+
action='store_true')
184+
185+
parser.add_argument('-h', '--help', action='help',
186+
default=argparse.SUPPRESS,
187+
help='show this help message and exit')
188+
189+
args = parser.parse_args()
190+
191+
if args.make_only:
192+
args.with_extended_buildtools = False
193+
194+
if args.debug:
195+
logger.setLevel(logging.DEBUG)
196+
elif args.quiet:
197+
logger.setLevel(logging.ERROR)
198+
199+
if args.url and args.filename:
200+
logger.debug("--url and --filename detected. Ignoring --base-url "
201+
"--release --installer-version arguments.")
202+
filename = args.filename
203+
buildtools_url = "%s/%s" % (args.url, filename)
204+
else:
205+
if args.base_url:
206+
base_url = args.base_url
207+
else:
208+
base_url = DEFAULT_BASE_URL
209+
if args.release:
210+
# check if this is a pre-release "milestone" SDK
211+
m = re.search(r"^(?P<distro>[a-zA-Z\-]+)(?P<version>[0-9.]+)(?P<milestone>_M[1-9])$",
212+
args.release)
213+
logger.debug("milestone regex: %s" % m)
214+
if m and m.group('milestone'):
215+
logger.debug("release[distro]: %s" % m.group('distro'))
216+
logger.debug("release[version]: %s" % m.group('version'))
217+
logger.debug("release[milestone]: %s" % m.group('milestone'))
218+
if not args.build_date:
219+
logger.error("Milestone installers require --build-date")
220+
else:
221+
if args.make_only:
222+
filename = "%s-buildtools-make-nativesdk-standalone-%s-%s.sh" % (
223+
arch, args.installer_version, args.build_date)
224+
elif args.with_extended_buildtools:
225+
filename = "%s-buildtools-extended-nativesdk-standalone-%s-%s.sh" % (
226+
arch, args.installer_version, args.build_date)
227+
else:
228+
filename = "%s-buildtools-nativesdk-standalone-%s-%s.sh" % (
229+
arch, args.installer_version, args.build_date)
230+
safe_filename = quote(filename)
231+
buildtools_url = "%s/milestones/%s/buildtools/%s" % (base_url, args.release, safe_filename)
232+
# regular release SDK
233+
else:
234+
if args.make_only:
235+
filename = "%s-buildtools-make-nativesdk-standalone-%s.sh" % (arch, args.installer_version)
236+
if args.with_extended_buildtools:
237+
filename = "%s-buildtools-extended-nativesdk-standalone-%s.sh" % (arch, args.installer_version)
238+
else:
239+
filename = "%s-buildtools-nativesdk-standalone-%s.sh" % (arch, args.installer_version)
240+
safe_filename = quote(filename)
241+
buildtools_url = "%s/%s/buildtools/%s" % (base_url, args.release, safe_filename)
242+
243+
sdk_dir = args.downloads_directory or tempfile.mkdtemp()
244+
os.makedirs(sdk_dir, exist_ok=True)
245+
try:
246+
# Fetch installer
247+
logger.info("Fetching buildtools installer")
248+
tmpbuildtools = os.path.join(sdk_dir, filename)
249+
with open(os.path.join(sdk_dir, 'buildtools_url'), 'w') as f:
250+
f.write(buildtools_url)
251+
ret = subprocess.call("wget -q -O %s %s" %
252+
(tmpbuildtools, buildtools_url), shell=True)
253+
if ret != 0:
254+
logger.error("Could not download file from %s" % buildtools_url)
255+
return ret
256+
257+
# Verify checksum
258+
if args.check:
259+
logger.info("Fetching buildtools installer checksum")
260+
checksum_type = "sha256sum"
261+
checksum_url = "{}.{}".format(buildtools_url, checksum_type)
262+
checksum_filename = "{}.{}".format(filename, checksum_type)
263+
tmpbuildtools_checksum = os.path.join(sdk_dir, checksum_filename)
264+
with open(os.path.join(sdk_dir, 'checksum_url'), 'w') as f:
265+
f.write(checksum_url)
266+
ret = subprocess.call("wget -q -O %s %s" %
267+
(tmpbuildtools_checksum, checksum_url), shell=True)
268+
if ret != 0:
269+
logger.error("Could not download file from %s" % checksum_url)
270+
return ret
271+
regex = re.compile(r"^(?P<checksum>[0-9a-f]+)\s+(?P<path>.*/)?(?P<filename>.*)$")
272+
with open(tmpbuildtools_checksum, 'rb') as f:
273+
original = f.read()
274+
m = re.search(regex, original.decode("utf-8"))
275+
logger.debug("checksum regex match: %s" % m)
276+
logger.debug("checksum: %s" % m.group('checksum'))
277+
logger.debug("path: %s" % m.group('path'))
278+
logger.debug("filename: %s" % m.group('filename'))
279+
if filename != m.group('filename'):
280+
logger.error("Filename does not match name in checksum")
281+
return 1
282+
checksum = m.group('checksum')
283+
checksum_value = sha256_file(tmpbuildtools)
284+
if checksum == checksum_value:
285+
logger.info("Checksum success")
286+
else:
287+
logger.error("Checksum %s expected. Actual checksum is %s." %
288+
(checksum, checksum_value))
289+
return 1
290+
291+
# Make installer executable
292+
logger.info("Making installer executable")
293+
st = os.stat(tmpbuildtools)
294+
os.chmod(tmpbuildtools, st.st_mode | stat.S_IEXEC)
295+
logger.debug(os.stat(tmpbuildtools))
296+
if args.directory:
297+
install_dir = os.path.abspath(args.directory)
298+
ret = subprocess.call("%s -d %s -y" %
299+
(tmpbuildtools, install_dir), shell=True)
300+
else:
301+
install_dir = "/opt/poky/%s" % args.installer_version
302+
ret = subprocess.call("%s -y" % tmpbuildtools, shell=True)
303+
if ret != 0:
304+
logger.error("Could not run buildtools installer")
305+
return ret
306+
307+
# Setup the environment
308+
logger.info("Setting up the environment")
309+
regex = re.compile(r'^(?P<export>export )?(?P<env_var>[A-Z_]+)=(?P<env_val>.+)$')
310+
with open("%s/environment-setup-%s-pokysdk-linux" %
311+
(install_dir, arch), 'rb') as f:
312+
for line in f:
313+
match = regex.search(line.decode('utf-8'))
314+
logger.debug("export regex: %s" % match)
315+
if match:
316+
env_var = match.group('env_var')
317+
logger.debug("env_var: %s" % env_var)
318+
env_val = remove_quotes(match.group('env_val'))
319+
logger.debug("env_val: %s" % env_val)
320+
os.environ[env_var] = env_val
321+
322+
# Test installation
323+
logger.info("Testing installation")
324+
tool = ""
325+
m = re.search("extended", tmpbuildtools)
326+
logger.debug("extended regex: %s" % m)
327+
if args.with_extended_buildtools and not m:
328+
logger.info("Ignoring --with-extended-buildtools as filename "
329+
"does not contain 'extended'")
330+
if args.make_only:
331+
tool = 'make'
332+
elif args.with_extended_buildtools and m:
333+
tool = 'gcc'
334+
else:
335+
tool = 'tar'
336+
logger.debug("install_dir: %s" % install_dir)
337+
cmd = shlex.split("/usr/bin/which %s" % tool)
338+
logger.debug("cmd: %s" % cmd)
339+
logger.debug("tool: %s" % tool)
340+
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
341+
output, errors = proc.communicate()
342+
logger.debug("proc.args: %s" % proc.args)
343+
logger.debug("proc.communicate(): output %s" % output)
344+
logger.debug("proc.communicate(): errors %s" % errors)
345+
which_tool = output.decode('utf-8')
346+
logger.debug("which %s: %s" % (tool, which_tool))
347+
ret = proc.returncode
348+
if not which_tool.startswith(install_dir):
349+
logger.error("Something went wrong: %s not found in %s" %
350+
(tool, install_dir))
351+
if ret != 0:
352+
logger.error("Something went wrong: installation failed")
353+
else:
354+
logger.info("Installation successful. Remember to source the "
355+
"environment setup script now and in any new session.")
356+
return ret
357+
358+
finally:
359+
# cleanup tmp directory
360+
if not args.downloads_directory:
361+
shutil.rmtree(sdk_dir)
362+
363+
364+
if __name__ == '__main__':
365+
try:
366+
ret = main()
367+
except Exception:
368+
ret = 1
369+
import traceback
370+
371+
traceback.print_exc()
372+
sys.exit(ret)
3373
push:
4374
branches:
5375
- main

0 commit comments

Comments
 (0)