-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbuildPackages.py
190 lines (155 loc) · 6.42 KB
/
buildPackages.py
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/usr/bin/env python2
from __future__ import print_function
import argparse
import logging
import os
import sys
import environment
from support import build
from support import buildsystem
from support import deps
from support import steps
from support import toolchain
VALID_ARCH_TARGETS = ('amd64', 'arm')
LOGGING_FORMAT = ('%(asctime)s %(module)-15s %(funcName)-20s %(levelname)-10s '
'%(message)s')
log = logging.getLogger()
def build_all(args, packages, env):
"""Takes an ordered list of packages and builds them one-by-one."""
me = os.getpid()
built = set()
notbuilt_deps = set()
notbuilt_failed = set()
for name, package in packages:
if ((not args.nodeps) and
(not set(package.build_requires()).issubset(built))):
log.error('Package "%s" build-depends not met.', name)
notbuilt_deps.add(name)
continue
if args.dryrun:
log.info('Would build package "%s" (dry run).', name)
built.add(name)
continue
elif args.only and name not in args.only:
log.info('Not building package "%s" (not in list of packages '
'to build).', name)
built.add(name)
continue
try:
env = env.copy()
# Prepare chroot for building this package.
# Don't re-build the image.
steps.create_chroot(env, False)
# Build!
build.build_package(package, env)
except SystemExit:
raise
except Exception:
log.exception('Building %s failed.', name)
notbuilt_failed.add(name)
else:
built.add(name)
# If tarball extraction fails, we'll end up here but a) in a child,
# and b) in the chroot.
if os.getpid() != me:
status = 0
if name in notbuilt_failed:
status = 1
exit(status)
for package in notbuilt_deps:
log.warning('package "%s" failed to build because of missing build '
'dependencies.', package)
for package in notbuilt_failed:
log.error('package "%s" failed to build.', package)
if notbuilt_deps or notbuilt_failed:
return 1
else:
return 0
def main(argv):
parser = argparse.ArgumentParser(description='Build ports for Pedigree.')
parser.add_argument('--target', type=str, choices=VALID_ARCH_TARGETS,
required=True, help='Architecture target for builds')
parser.add_argument('--dryrun', action='store_true',
help='Do a dry run: only print the packages that '
'would be built')
parser.add_argument('--only', type=str, nargs='+', required=False,
help='Only build the given packages. Build-depends '
'will not be built so if this may not create stable '
'or successful builds.')
parser.add_argument('--only-depends', type=str, nargs='+', required=False,
help='Only build the given packages and their '
'dependencies.')
parser.add_argument('--nodeps', action='store_true',
help='Ignore missing build dependencies.')
parser.add_argument('--logfile', type=str, required=False,
help='File to write logs to. stdout will be used if '
'this is not provided.')
parser.add_argument('--logformat', type=str, default=LOGGING_FORMAT,
help='Log entry format.')
parser.add_argument('--debug', action='store_true', default=False,
help='Whether to enable debug logging.')
parser.add_argument('--buildimages', action='store_true', default=False,
help='Whether to build Docker images, or just run '
'them.')
args = parser.parse_args()
# Set up the root logger.
kwargs = {}
if args.logfile:
kwargs['filename'] = args.logfile
with open(args.logfile, 'w'):
pass
else:
# Clone stderr so forked children can have their stderr redirected.
# This allows us to log stdout/stderr from all subprocesses but still
# have all logging calls end up going to the true stderr.
# This is only necessary if we aren't already writing logs to a file.
stderr_dup = os.dup(sys.stderr.fileno())
log_stream = os.fdopen(stderr_dup, 'w')
kwargs['stream'] = log_stream
if args.debug:
kwargs['level'] = logging.DEBUG
else:
kwargs['level'] = logging.INFO
kwargs['format'] = args.logformat
logging.basicConfig(**kwargs)
# Load up an environment ready for building.
env = environment.generate_environment(args.target)
# Drop privileges ASAP if we got run as root.
if not os.getuid():
# Release privileges.
os.setgroups([])
os.setgid(int(env['UNPRIVILEGED_GID']))
os.setuid(int(env['UNPRIVILEGED_UID']))
if not args.dryrun:
# Make sure we have a sane toolchain with a useful chroot spec file.
toolchain.chroot_spec(env)
# Set up our local pup config.
toolchain.prepare_package_manager(env)
# Prepare our chroot in which building will happen.
# Don't let this modify our environment just yet.
steps.create_chroot(env.copy(), args.buildimages)
# Prepare the cross-toolchain for building. This includes preparing the
# correct location for libc/libm, libpedigree, etc
toolchain.prepare_compiler(env)
else:
log.info('not touching filesystem, dry run')
# Get packages to build.
packages = buildsystem.load_packages(env)
# Sort dependencies so the build is performed correctly.
packages = deps.sort_dependencies(packages)
# Filter based on only/only-depends.
if args.only_depends:
actual_packages = []
wanted_depends = set()
for name, package in reversed(packages):
if name in args.only_depends or name in wanted_depends:
actual_packages.append((name, package))
wanted_depends.update(package.build_requires())
packages = tuple(reversed(actual_packages))
# Build packages.
result = build_all(args, packages, env)
# All done with logging.
logging.shutdown()
return result
if __name__ == '__main__':
exit(main(sys.argv))