3 # Buildtools and buildtools extended installer helper script
5 # Copyright (C) 2017-2020 Intel Corporation
7 # SPDX-License-Identifier: GPL-2.0-only
9 # NOTE: --with-extended-buildtools is on by default
11 # Example usage (extended buildtools from milestone):
12 # (1) using --url and --filename
13 # $ install-buildtools \
14 # --url http://downloads.yoctoproject.org/releases/yocto/milestones/yocto-3.1_M3/buildtools \
15 # --filename x86_64-buildtools-extended-nativesdk-standalone-3.0+snapshot-20200315.sh
16 # (2) using --base-url, --release, --installer-version and --build-date
17 # $ install-buildtools \
18 # --base-url http://downloads.yoctoproject.org/releases/yocto \
19 # --release yocto-3.1_M3 \
20 # --installer-version 3.0+snapshot
21 # --build-date 202000315
23 # Example usage (standard buildtools from release):
24 # (3) using --url and --filename
25 # $ install-buildtools --without-extended-buildtools \
26 # --url http://downloads.yoctoproject.org/releases/yocto/yocto-3.0.2/buildtools \
27 # --filename x86_64-buildtools-nativesdk-standalone-3.0.2.sh
28 # (4) using --base-url, --release and --installer-version
29 # $ install-buildtools --without-extended-buildtools \
30 # --base-url http://downloads.yoctoproject.org/releases/yocto \
31 # --release yocto-3.0.2 \
32 # --installer-version 3.0.2
46 from urllib
.parse
import quote
48 scripts_path
= os
.path
.dirname(os
.path
.realpath(__file__
))
49 lib_path
= scripts_path
+ '/lib'
50 sys
.path
= sys
.path
+ [lib_path
]
55 PROGNAME
= 'install-buildtools'
56 logger
= scriptutils
.logger_create(PROGNAME
, stream
=sys
.stdout
)
58 DEFAULT_INSTALL_DIR
= os
.path
.join(os
.path
.split(scripts_path
)[0],'buildtools')
59 DEFAULT_BASE_URL
= 'http://downloads.yoctoproject.org/releases/yocto'
60 DEFAULT_RELEASE
= 'yocto-4.1'
61 DEFAULT_INSTALLER_VERSION
= '4.1'
62 DEFAULT_BUILDDATE
= '202110XX'
64 # Python version sanity check
65 if not (sys
.version_info
.major
== 3 and sys
.version_info
.minor
>= 4):
66 logger
.error("This script requires Python 3.4 or greater")
67 logger
.error("You have Python %s.%s" %
68 (sys
.version_info
.major
, sys
.version_info
.minor
))
71 # The following three functions are copied directly from
72 # bitbake/lib/bb/utils.py, in order to allow this script
73 # to run on versions of python earlier than what bitbake
74 # supports (e.g. less than Python 3.5 for YP 3.1 release)
76 def _hasher(method
, filename
):
79 with
open(filename
, "rb") as f
:
81 with mmap
.mmap(f
.fileno(), 0, access
=mmap
.ACCESS_READ
) as mm
:
82 for chunk
in iter(lambda: mm
.read(8192), b
''):
85 # You can't mmap() an empty file so silence this exception
87 return method
.hexdigest()
90 def md5_file(filename
):
92 Return the hex string representation of the MD5 checksum of filename.
95 return _hasher(hashlib
.md5(), filename
)
97 def sha256_file(filename
):
99 Return the hex string representation of the 256-bit SHA checksum of
103 return _hasher(hashlib
.sha256(), filename
)
107 global DEFAULT_INSTALL_DIR
108 global DEFAULT_BASE_URL
109 global DEFAULT_RELEASE
110 global DEFAULT_INSTALLER_VERSION
111 global DEFAULT_BUILDDATE
116 arch
= platform
.machine()
118 parser
= argparse
.ArgumentParser(
119 description
="Buildtools installation helper",
121 parser
.add_argument('-u', '--url',
122 help='URL from where to fetch buildtools SDK installer, not '
123 'including filename (optional)\n'
124 'Requires --filename.',
126 parser
.add_argument('-f', '--filename',
127 help='filename for the buildtools SDK installer to be installed '
128 '(optional)\nRequires --url',
130 parser
.add_argument('-d', '--directory',
131 default
=DEFAULT_INSTALL_DIR
,
132 help='directory where buildtools SDK will be installed (optional)',
134 parser
.add_argument('-r', '--release',
135 default
=DEFAULT_RELEASE
,
136 help='Yocto Project release string for SDK which will be '
137 'installed (optional)',
139 parser
.add_argument('-V', '--installer-version',
140 default
=DEFAULT_INSTALLER_VERSION
,
141 help='version string for the SDK to be installed (optional)',
143 parser
.add_argument('-b', '--base-url',
144 default
=DEFAULT_BASE_URL
,
145 help='base URL from which to fetch SDK (optional)', action
='store')
146 parser
.add_argument('-t', '--build-date',
147 default
=DEFAULT_BUILDDATE
,
148 help='Build date of pre-release SDK (optional)', action
='store')
149 group
= parser
.add_mutually_exclusive_group()
150 group
.add_argument('--with-extended-buildtools', action
='store_true',
151 dest
='with_extended_buildtools',
153 help='enable extended buildtools tarball (on by default)')
154 group
.add_argument('--without-extended-buildtools', action
='store_false',
155 dest
='with_extended_buildtools',
156 help='disable extended buildtools (traditional buildtools tarball)')
157 group
.add_argument('--make-only', action
='store_true',
158 help='only install make tarball')
159 group
= parser
.add_mutually_exclusive_group()
160 group
.add_argument('-c', '--check', help='enable checksum validation',
161 default
=True, action
='store_true')
162 group
.add_argument('-n', '--no-check', help='disable checksum validation',
163 dest
="check", action
='store_false')
164 parser
.add_argument('-D', '--debug', help='enable debug output',
166 parser
.add_argument('-q', '--quiet', help='print only errors',
169 parser
.add_argument('-h', '--help', action
='help',
170 default
=argparse
.SUPPRESS
,
171 help='show this help message and exit')
173 args
= parser
.parse_args()
176 args
.with_extended_buildtools
= False
179 logger
.setLevel(logging
.DEBUG
)
181 logger
.setLevel(logging
.ERROR
)
183 if args
.url
and args
.filename
:
184 logger
.debug("--url and --filename detected. Ignoring --base-url "
185 "--release --installer-version arguments.")
186 filename
= args
.filename
187 buildtools_url
= "%s/%s" % (args
.url
, filename
)
190 base_url
= args
.base_url
192 base_url
= DEFAULT_BASE_URL
194 # check if this is a pre-release "milestone" SDK
195 m
= re
.search(r
"^(?P<distro>[a-zA-Z\-]+)(?P<version>[0-9.]+)(?P<milestone>_M[1-9])$",
197 logger
.debug("milestone regex: %s" % m
)
198 if m
and m
.group('milestone'):
199 logger
.debug("release[distro]: %s" % m
.group('distro'))
200 logger
.debug("release[version]: %s" % m
.group('version'))
201 logger
.debug("release[milestone]: %s" % m
.group('milestone'))
202 if not args
.build_date
:
203 logger
.error("Milestone installers require --build-date")
206 filename
= "%s-buildtools-make-nativesdk-standalone-%s-%s.sh" % (
207 arch
, args
.installer_version
, args
.build_date
)
208 elif args
.with_extended_buildtools
:
209 filename
= "%s-buildtools-extended-nativesdk-standalone-%s-%s.sh" % (
210 arch
, args
.installer_version
, args
.build_date
)
212 filename
= "%s-buildtools-nativesdk-standalone-%s-%s.sh" % (
213 arch
, args
.installer_version
, args
.build_date
)
214 safe_filename
= quote(filename
)
215 buildtools_url
= "%s/milestones/%s/buildtools/%s" % (base_url
, args
.release
, safe_filename
)
216 # regular release SDK
219 filename
= "%s-buildtools-make-nativesdk-standalone-%s.sh" % (arch
, args
.installer_version
)
220 if args
.with_extended_buildtools
:
221 filename
= "%s-buildtools-extended-nativesdk-standalone-%s.sh" % (arch
, args
.installer_version
)
223 filename
= "%s-buildtools-nativesdk-standalone-%s.sh" % (arch
, args
.installer_version
)
224 safe_filename
= quote(filename
)
225 buildtools_url
= "%s/%s/buildtools/%s" % (base_url
, args
.release
, safe_filename
)
227 tmpsdk_dir
= tempfile
.mkdtemp()
230 logger
.info("Fetching buildtools installer")
231 tmpbuildtools
= os
.path
.join(tmpsdk_dir
, filename
)
232 ret
= subprocess
.call("wget -q -O %s %s" %
233 (tmpbuildtools
, buildtools_url
), shell
=True)
235 logger
.error("Could not download file from %s" % buildtools_url
)
240 logger
.info("Fetching buildtools installer checksum")
242 for checksum_type
in ["md5sum", "sha256sum"]:
243 check_url
= "{}.{}".format(buildtools_url
, checksum_type
)
244 checksum_filename
= "{}.{}".format(filename
, checksum_type
)
245 tmpbuildtools_checksum
= os
.path
.join(tmpsdk_dir
, checksum_filename
)
246 ret
= subprocess
.call("wget -q -O %s %s" %
247 (tmpbuildtools_checksum
, check_url
), shell
=True)
252 logger
.error("Could not download file from %s" % check_url
)
254 regex
= re
.compile(r
"^(?P<checksum>[0-9a-f]+)\s+(?P<path>.*/)?(?P<filename>.*)$")
255 with
open(tmpbuildtools_checksum
, 'rb') as f
:
257 m
= re
.search(regex
, original
.decode("utf-8"))
258 logger
.debug("checksum regex match: %s" % m
)
259 logger
.debug("checksum: %s" % m
.group('checksum'))
260 logger
.debug("path: %s" % m
.group('path'))
261 logger
.debug("filename: %s" % m
.group('filename'))
262 if filename
!= m
.group('filename'):
263 logger
.error("Filename does not match name in checksum")
265 checksum
= m
.group('checksum')
266 if checksum_type
== "md5sum":
267 checksum_value
= md5_file(tmpbuildtools
)
269 checksum_value
= sha256_file(tmpbuildtools
)
270 if checksum
== checksum_value
:
271 logger
.info("Checksum success")
273 logger
.error("Checksum %s expected. Actual checksum is %s." %
274 (checksum
, checksum_value
))
277 # Make installer executable
278 logger
.info("Making installer executable")
279 st
= os
.stat(tmpbuildtools
)
280 os
.chmod(tmpbuildtools
, st
.st_mode | stat
.S_IEXEC
)
281 logger
.debug(os
.stat(tmpbuildtools
))
283 install_dir
= args
.directory
284 ret
= subprocess
.call("%s -d %s -y" %
285 (tmpbuildtools
, install_dir
), shell
=True)
287 install_dir
= "/opt/poky/%s" % args
.installer_version
288 ret
= subprocess
.call("%s -y" % tmpbuildtools
, shell
=True)
290 logger
.error("Could not run buildtools installer")
293 # Setup the environment
294 logger
.info("Setting up the environment")
295 regex
= re
.compile(r
'^(?P<export>export )?(?P<env_var>[A-Z_]+)=(?P<env_val>.+)$')
296 with
open("%s/environment-setup-%s-pokysdk-linux" %
297 (install_dir
, arch
), 'rb') as f
:
299 match
= regex
.search(line
.decode('utf-8'))
300 logger
.debug("export regex: %s" % match
)
302 env_var
= match
.group('env_var')
303 logger
.debug("env_var: %s" % env_var
)
304 env_val
= match
.group('env_val')
305 logger
.debug("env_val: %s" % env_val
)
306 os
.environ
[env_var
] = env_val
309 logger
.info("Testing installation")
311 m
= re
.search("extended", tmpbuildtools
)
312 logger
.debug("extended regex: %s" % m
)
313 if args
.with_extended_buildtools
and not m
:
314 logger
.info("Ignoring --with-extended-buildtools as filename "
315 "does not contain 'extended'")
318 elif args
.with_extended_buildtools
and m
:
322 logger
.debug("install_dir: %s" % install_dir
)
323 cmd
= shlex
.split("/usr/bin/which %s" % tool
)
324 logger
.debug("cmd: %s" % cmd
)
325 logger
.debug("tool: %s" % tool
)
326 proc
= subprocess
.Popen(cmd
, stdout
=subprocess
.PIPE
)
327 output
, errors
= proc
.communicate()
328 logger
.debug("proc.args: %s" % proc
.args
)
329 logger
.debug("proc.communicate(): output %s" % output
)
330 logger
.debug("proc.communicate(): errors %s" % errors
)
331 which_tool
= output
.decode('utf-8')
332 logger
.debug("which %s: %s" % (tool
, which_tool
))
333 ret
= proc
.returncode
334 if not which_tool
.startswith(install_dir
):
335 logger
.error("Something went wrong: %s not found in %s" %
338 logger
.error("Something went wrong: installation failed")
340 logger
.info("Installation successful. Remember to source the "
341 "environment setup script now and in any new session.")
345 # cleanup tmp directory
346 shutil
.rmtree(tmpsdk_dir
)
349 if __name__
== '__main__':
356 traceback
.print_exc()