Skip to content

Commit 24ec16e

Browse files
committed
Implement building from source package
Currently the build scripts require the LLVM and newlib sources to be git repositories (e.g. the scripts query git commit hashes). This patch adds a new mode for building the toolchain from a source package in which the scripts, LLVM and newlib are bundled together. The file versions.yml is now used for distinguishing between the two modes. When the top-level entry 'SourceType' is set to 'standalone-build-scripts' the scripts will assume that LLVM and newlib need to be checked out from git. Otherwise, if the value is set to 'source-package' the scripts will assume that LLVM and newlib sources are located in the same directory as the versions.yml file. When building a source package the scripts generate versions.yml and set SourceType to 'source-package'. Also, when build from a source package the file VERSION.txt is copied from the source package rather than generated on the fly. The following command-line options are incompatible with the 'source-package' mode: - prepare - package-src - --repositories-dir - -r/--revision If any of these options is specified when build from a source package the scripts will ignore the option and print a warning.
1 parent 130a4d0 commit 24ec16e

File tree

6 files changed

+170
-76
lines changed

6 files changed

+170
-76
lines changed

scripts/build.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ def parse_args_to_config() -> Config:
4747
)
4848
parser.add_argument('-v', '--verbose', help='log more information',
4949
action='store_true')
50-
parser.add_argument('-r', '--revision', metavar='R', default='13.0.0',
51-
help='revision to build (default: 13.0.0)')
50+
parser.add_argument('-r', '--revision', metavar='R',
51+
help='revision to build '
52+
'(default: {})'.format(config.DEFAULT_REVISION))
5253
variant_names = sorted(config.LIBRARY_SPECS.keys())
5354
parser.add_argument('--variants', metavar='VAR', nargs='+',
5455
choices=variant_names + ['all'], default=['all'],
@@ -176,6 +177,7 @@ def prepare_repositories(cfg: config.Config,
176177
"""Prepare source repositories according to the selected --checkout-mode
177178
option and the current state of repositories.
178179
"""
180+
assert not cfg.is_source_package
179181
patches_dir = os.path.join(cfg.source_dir, 'patches')
180182

181183
# Determine which git action to perform
@@ -330,13 +332,15 @@ def ask_about_runtime_dlls(cfg: Config) -> Optional[bool]:
330332
def main() -> int:
331333
util.configure_logging()
332334
cfg = parse_args_to_config()
333-
versions = repos.get_all_versions(os.path.join(cfg.source_dir,
334-
'versions.yml'))
335-
if cfg.revision not in versions:
336-
logging.error('Invalid revision %s', cfg.revision)
337-
return 1
338-
339-
version = versions[cfg.revision]
335+
if cfg.is_source_package:
336+
version = None
337+
else:
338+
versions = repos.get_all_versions(os.path.join(cfg.source_dir,
339+
'versions.yml'))
340+
if cfg.revision not in versions:
341+
logging.error('Invalid revision %s', cfg.revision)
342+
return 1
343+
version = versions[cfg.revision]
340344

341345
try:
342346
if (cfg.host_toolchain.kind == config.ToolchainKind.MINGW
@@ -348,8 +352,11 @@ def main() -> int:
348352

349353
if not cfg.skip_checks:
350354
check.check_prerequisites(cfg)
351-
run_or_skip(cfg, Action.PREPARE,
352-
lambda: prepare_repositories(cfg, version),
355+
356+
def do_prepare_repositories():
357+
assert version is not None
358+
prepare_repositories(cfg, version)
359+
run_or_skip(cfg, Action.PREPARE, do_prepare_repositories,
353360
'source code checkout')
354361
build_all(cfg)
355362
run_tests(cfg)
@@ -361,7 +368,8 @@ def do_source_package():
361368
'source package')
362369

363370
def do_binary_package():
364-
version.poplulate_commits(cfg.repos_dir)
371+
if version is not None:
372+
version.poplulate_commits(cfg.repos_dir)
365373
package.create_binary_package(cfg, version)
366374
run_or_skip(cfg, Action.PACKAGE, do_binary_package, 'binary package')
367375

scripts/config.py

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@
2121
from typing import Optional, Set, TYPE_CHECKING
2222

2323
import execution
24+
import util
25+
26+
DEFAULT_REVISION = '13.0.0'
27+
28+
29+
@enum.unique
30+
class SourceType(enum.Enum):
31+
"""Enumeration for the SourceType value in versions.yml:
32+
STANDALONE - the source contains just the build scripts, LLVM and newlib
33+
need to be checked out separately
34+
SOURCE_PACKAGE - the source contains LLVM and newlib bundled with the
35+
build scripts"""
36+
STANDALONE = 'standalone-build-scripts'
37+
SOURCE_PACKAGE = 'source-package'
2438

2539

2640
@enum.unique
@@ -235,6 +249,11 @@ def _assign_dir(arg, default, rev):
235249
return os.path.abspath(res)
236250

237251

252+
def _warn_source_package_unsupported(feature: str) -> None:
253+
logging.warning('%s is not supported when building from '
254+
'source package, ignoring', feature)
255+
256+
238257
class Config: # pylint: disable=too-many-instance-attributes
239258
# pylint: disable=too-few-public-methods
240259
"""Configuration for the whole build process"""
@@ -257,10 +276,20 @@ def _default_target(self):
257276
return 'aarch64-none-elf'
258277
return None
259278

279+
def _configure_source_type(self, args: argparse.Namespace):
280+
self.source_dir = os.path.abspath(args.source_dir)
281+
source_yaml = util.read_yaml(os.path.join(self.source_dir,
282+
'versions.yml'))
283+
self.source_type = SourceType(source_yaml['SourceType'])
284+
if self.source_type == SourceType.SOURCE_PACKAGE:
285+
self.revision = source_yaml['Revision']
286+
260287
def _configure_actions(self, args: argparse.Namespace):
261288
if not args.actions or Action.ALL.value in args.actions:
262289
# Actions that are not part of the "ALL" action:
263290
exclude_from_all = [Action.ALL, Action.TEST, Action.PACKAGE_SRC]
291+
if self.is_source_package:
292+
exclude_from_all.append(Action.PREPARE)
264293
self.actions = set(action for action in Action
265294
if action not in exclude_from_all)
266295
for action in [Action.TEST, Action.PACKAGE_SRC]:
@@ -269,6 +298,13 @@ def _configure_actions(self, args: argparse.Namespace):
269298
else:
270299
self.actions = set(Action(act_str) for act_str in args.actions)
271300

301+
if self.is_source_package:
302+
for action in [Action.PREPARE, Action.PACKAGE_SRC]:
303+
if action.value in args.actions:
304+
_warn_source_package_unsupported(
305+
'action "{}"'.format(action.value))
306+
self.actions.remove(action)
307+
272308
def _configure_toolchains(self, args: argparse.Namespace):
273309
# According to
274310
# https://docs.python.org/3.6/library/enum.html#using-a-custom-new:
@@ -292,18 +328,27 @@ def _configure_toolchains(self, args: argparse.Namespace):
292328
self.is_cross_compiling = (os.name == 'posix' and self.is_windows)
293329

294330
def _fill_args(self, args: argparse.Namespace):
295-
if 'all' in args.variants:
296-
variant_names = LIBRARY_SPECS.keys()
297-
else:
298-
variant_names = set(args.variants)
331+
variant_names = (LIBRARY_SPECS.keys() if 'all' in args.variants
332+
else set(args.variants))
299333
self.variants = [LIBRARY_SPECS[v] for v in sorted(variant_names)]
300334

301335
self.default_target = self._default_target()
302336

303-
rev = args.revision
304-
self.revision = rev
305-
self.source_dir = os.path.abspath(args.source_dir)
306-
self.repos_dir = _assign_dir(args.repositories_dir, 'repos', rev)
337+
if self.is_source_package:
338+
# For SOURCE_PACKAGE self.revision is set in _configure_source_type
339+
assert self.revision is not None
340+
rev = self.revision
341+
self.repos_dir = self.source_dir
342+
if args.repositories_dir is not None:
343+
_warn_source_package_unsupported('--repositories-dir')
344+
if args.revision is not None:
345+
_warn_source_package_unsupported('--revision')
346+
else:
347+
rev = (args.revision if args.revision is not None
348+
else DEFAULT_REVISION)
349+
self.revision = rev
350+
self.repos_dir = _assign_dir(args.repositories_dir, 'repos', rev)
351+
307352
self.build_dir = _assign_dir(args.build_dir, 'build', rev)
308353
self.install_dir = _assign_dir(args.install_dir, 'install', rev)
309354
self.package_dir = os.path.abspath(args.package_dir)
@@ -346,6 +391,14 @@ def copy_runtime_dlls(self) -> bool:
346391
def copy_runtime_dlls(self, value: bool) -> None:
347392
self._copy_runtime_dlls = value
348393

394+
@property
395+
def is_source_package(self) -> bool:
396+
"""True iff building from a bundled source (i.e. build scripts, LLVM
397+
and newlib) rather than a repository containing just the build
398+
scripts."""
399+
assert self.source_type is not None
400+
return self.source_type == SourceType.SOURCE_PACKAGE
401+
349402
def _fill_inferred(self):
350403
"""Fill in additional fields that can be inferred from the
351404
configuration, but are still useful for convenience."""
@@ -386,6 +439,7 @@ def _fill_inferred(self):
386439

387440
def __init__(self, args: argparse.Namespace):
388441
self._copy_runtime_dlls = None
442+
self._configure_source_type(args)
389443
self._configure_actions(args)
390444
self._configure_toolchains(args)
391445
self._fill_args(args)

scripts/package.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import logging
1717
import os
1818
import shutil
19-
from typing import FrozenSet
19+
from typing import FrozenSet, Optional
2020
import tarfile
2121
import zipfile
2222

@@ -30,9 +30,10 @@ def _write_version_file(cfg: config.Config, version: repos.LLVMBMTC,
3030
target_dir: str) -> None:
3131
"""Create VERSION.txt in the install directory."""
3232
dest = os.path.join(target_dir, 'VERSION.txt')
33+
toolchain_ver = 'LLVM Embedded Toolchain for Arm ' + cfg.version_string
3334
if cfg.verbose:
34-
logging.info('Writing "%s" to %s', cfg.version_string, dest)
35-
lines = [cfg.version_string, '', 'Components:']
35+
logging.info('Writing "%s" to %s', toolchain_ver, dest)
36+
lines = [toolchain_ver, '', 'Components:']
3637
for name in sorted(version.modules.keys()):
3738
comp_info = version.modules[name].checkout_info
3839
lines.append('* {}'.format(comp_info))
@@ -41,6 +42,17 @@ def _write_version_file(cfg: config.Config, version: repos.LLVMBMTC,
4142
util.write_lines(lines, dest)
4243

4344

45+
def _write_versions_yml(cfg: config.Config, dest_dir: str) -> None:
46+
"""Create versions.yml for a source package."""
47+
dest_file = os.path.join(dest_dir, 'versions.yml')
48+
logging.info('Creating %s', dest_file)
49+
with open(dest_file, 'wt') as out_f:
50+
source_type = config.SourceType.SOURCE_PACKAGE.value
51+
out_f.write('---\n'
52+
'SourceType: "{}"\n'
53+
'Revision: "{}"\n'.format(source_type, cfg.revision))
54+
55+
4456
def _copy_samples(cfg: config.Config) -> None:
4557
"""Copy code samples, filter out files that are not usable on the target
4658
platform."""
@@ -136,9 +148,18 @@ def _create_archive(cfg: config.Config, pkg_src_dir, pkg_dest_name) -> None:
136148
raise util.ToolchainBuildError from ex
137149

138150

139-
def create_binary_package(cfg: config.Config, version: repos.LLVMBMTC) -> None:
151+
def create_binary_package(cfg: config.Config,
152+
version: Optional[repos.LLVMBMTC]) -> None:
140153
"""Create a binary package with a newly built toolchain."""
141-
_write_version_file(cfg, version, cfg.target_llvm_dir)
154+
if cfg.is_source_package:
155+
ver_file_src = os.path.join(cfg.source_dir, 'VERSION.txt')
156+
ver_file_dest = os.path.join(cfg.target_llvm_dir, 'VERSION.txt')
157+
logging.info('Copying version file from %s to %s', ver_file_src,
158+
ver_file_dest)
159+
shutil.copy2(ver_file_src, ver_file_dest)
160+
else:
161+
assert version is not None
162+
_write_version_file(cfg, version, cfg.target_llvm_dir)
142163
_copy_samples(cfg)
143164
_create_archive(cfg, cfg.target_llvm_dir, cfg.bin_package_base_name)
144165

@@ -153,4 +174,5 @@ def create_source_package(cfg: config.Config, version: repos.LLVMBMTC) -> None:
153174
repos.export_toolchain_repositories(cfg.repos_dir, version,
154175
cfg.install_src_subdir)
155176
_write_version_file(cfg, version, cfg.install_src_subdir)
177+
_write_versions_yml(cfg, cfg.install_src_subdir)
156178
_create_archive(cfg, cfg.install_src_subdir, cfg.src_package_base_name)

scripts/repos.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
from typing import List, Mapping, Optional, Any
2626

2727
import git # type: ignore
28-
import yaml
2928

3029
import util
3130

@@ -158,17 +157,13 @@ def poplulate_commits(self, repos_dir: str) -> None:
158157
def get_all_versions(filename: str) -> Mapping[str, LLVMBMTC]:
159158
"""Build the database containing all releases from a YAML file."""
160159
versions = {}
161-
with open(filename, 'r') as stream:
162-
try:
163-
yml = yaml.load(stream, Loader=yaml.FullLoader)
164-
for value in yml:
165-
toolchain = LLVMBMTC(value)
166-
if toolchain.revision in versions:
167-
die('toolchain revision {} previously defined'.format(
168-
toolchain.revision))
169-
versions[toolchain.revision] = toolchain
170-
except yaml.YAMLError as ex:
171-
logging.error(ex)
160+
yml = util.read_yaml(filename)
161+
for value in yml['Revisions']:
162+
toolchain = LLVMBMTC(value)
163+
if toolchain.revision in versions:
164+
die('toolchain revision {} previously defined'.format(
165+
toolchain.revision))
166+
versions[toolchain.revision] = toolchain
172167

173168
return versions
174169

scripts/util.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import logging
1717
from typing import Any, Sequence, List
1818

19+
import yaml
20+
1921

2022
class ToolchainBuildError(RuntimeError):
2123
"""A single error for all failures related to toolchain build: this
@@ -39,3 +41,13 @@ def configure_logging() -> None:
3941
"""Set logging format and level threshold for the default logger."""
4042
log_format = '%(levelname)s: %(message)s'
4143
logging.basicConfig(format=log_format, level=logging.INFO)
44+
45+
46+
def read_yaml(path: str) -> Any:
47+
"""Read a YAML file and return its contents as a Python dict or list."""
48+
with open(path, 'rt') as in_f:
49+
try:
50+
return yaml.load(in_f, Loader=yaml.FullLoader)
51+
except yaml.YAMLError as ex:
52+
logging.error(ex)
53+
raise

0 commit comments

Comments
 (0)