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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
|
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Copyright © 2019 ANSSI. All rights reserved.
# Distributed under the terms of the GNU General Public License v2
# NOTICE: This eclass was pruned in order to keep only parts needed for CLIP OS.
# The base version was: https://chromium.googlesource.com/chromiumos/overlays/chromiumos-overlay/+/452e94ac38023445938ee6bde7f6a8762f9e4d28/eclass/cros-workon.eclass
# @ECLASS: cros-workon.eclass
# @MAINTAINER:
# ChromiumOS Build Team
# @BUGREPORTS:
# Please report bugs via http://crbug.com/new (with label Build)
# @VCSURL: https://chromium.googlesource.com/chromiumos/overlays/chromiumos-overlay/+/master/eclass/@ECLASS@
# @BLURB: helper eclass for building ChromiumOS packages from git
# @DESCRIPTION:
# A lot of ChromiumOS packages (src/platform/ and src/third_party/) are
# managed in the same way. You've got a git tree and you want to build
# it. This automates a lot of that common stuff in one place.
# Array variables. All of the following variables can contain multiple items
# with the restriction being that all of them have to have either:
# - the same number of items globally
# - one item as default for all
# - no items as the cros-workon default
# The exceptions are:
# - CROS_WORKON_PROJECT has to have all items specified.
# - CROS_WORKON_TREE is not listed here because it may not have the same number
# of items as other array variables when CROS_WORKON_SUBTREE is used.
# See the variable description below for more details.
ARRAY_VARIABLES=( CROS_WORKON_{SUBTREE,REPO,PROJECT,LOCALNAME,DESTDIR,COMMIT} )
# @ECLASS-VARIABLE: CROS_WORKON_SUBTREE
# @DESCRIPTION:
# Subpaths of the source checkout to be used in the build, separated by
# whitespace. Normally this will be set to directories, but files are also
# allowed if necessary.
# Default value is an empty string, meaning the whole source checkout is used.
# It is strongly recommended to set this variable if the source checkout
# contains multiple packages (e.g. platform2) to avoid unnecessary uprev when
# unrelated files in the repository are modified.
# Access to files outside of these subpaths will be denied.
: ${CROS_WORKON_SUBTREE:=}
# @ECLASS-VARIABLE: CROS_WORKON_REPO
# @DESCRIPTION:
# The base git URL to locate the remote repository. This is usually the root of
# the GoB server. It could be any git server, but for infra reliability, our
# policy is to only refer to servers we maintain (e.g. googlesource.com).
# It is combined with CROS_WORKON_PROJECT to form the full URL.
# Look at the cros-constants eclass for common values.
: ${CROS_WORKON_REPO:=/mnt/src}
# @ECLASS-VARIABLE: CROS_WORKON_PROJECT
# @DESCRIPTION:
# The path on the remote server (beneath CROS_WORKON_REPO) to find the git repo.
# This has no relationship to where the source is checked out locally in the
# manifest. If looking at a manifest.xml, this is the "name" attribute of the
# "project" tag.
: ${CROS_WORKON_PROJECT:=}
# @ECLASS-VARIABLE: CROS_WORKON_LOCALNAME
# @DESCRIPTION:
# The relative path in the local manifest checkout to find the local git
# checkout. The exact path it is relative to depends on the CATEGORY of the
# ebuild. For chromeos-base packages, this is relative to src/. For all other
# packages, it is relative to src/third_party/. This applies to all ebuilds
# regardless of the overlay they live in.
# If looking at a manifest.xml, this is related to the "path" attribute of the
# "project" tag (although that path is relative to the root of the manifest).
: ${CROS_WORKON_LOCALNAME:=${PN}}
# @ECLASS-VARIABLE: CROS_WORKON_DESTDIR
# @DESCRIPTION:
# Destination directory in ${WORKDIR} for checkout. It must be under ${S}.
# Note that the default is ${S}, but is only referenced in src_unpack for
# ebuilds that would like to override it.
: ${CROS_WORKON_DESTDIR:=}
# @ECLASS-VARIABLE: CROS_WORKON_COMMIT
# @DESCRIPTION:
# Git commit hashes of the source repositories.
# It is guaranteed that files identified by tree hashes in CROS_WORKON_TREE
# can be found in the commit.
# CROW_WORKON_COMMIT is updated only when CROS_WORKON_TREE below is updated,
# so it does not necessarily point to HEAD in the source repository.
: ${CROS_WORKON_COMMIT:=master}
# @ECLASS-VARIABLE: CROS_WORKON_TREE
# @DESCRIPTION:
# Git tree hashes of the contents of the source repositories.
# If CROS_WORKON_SUBTREE is set, tree hashes are taken from specified subpaths;
# otherwise, they are taken from the root directories of the source
# repositories. Therefore note that CROS_WORKON_TREE may have different number
# of entries than CROS_WORKON_COMMIT if multiple subpaths are specified in
# CROS_WORKON_SUBTREE.
# This is used for verifying the correctness of prebuilts. Unlike the commit
# hash, this hash is unaffected by the history of the repository, or by
# commit messages.
: ${CROS_WORKON_TREE:=}
# Scalar variables. These variables modify the behaviour of the eclass.
# @ECLASS-VARIABLE: CROS_WORKON_SUBDIRS_TO_COPY
# @DESCRIPTION:
# Make cros-workon operate exclusively with the subtrees given by this array.
# NOTE: This only speeds up local_cp builds. Inplace/local_git builds are unaffected.
# It will also be disabled by using project arrays, rather than a single project.
: ${CROS_WORKON_SUBDIRS_TO_COPY:=/}
# @ECLASS-VARIABLE: CROS_WORKON_SUBDIRS_TO_REV
# @DESCRIPTION:
# Array of directories in the source tree. If defined, this causes this ebuild
# to only uprev if there are changes within the specified subdirectories.
: ${CROS_WORKON_SUBDIRS_TO_REV:=/}
# We do not need to inherit git-r3 as we don't use any function provided.
DEPEND=">=dev-vcs/git-1.8.2.1[curl]"
# Sanitize all variables, autocomplete where necessary.
# This function possibly modifies all CROS_WORKON_ variables inplace. It also
# provides a global project_count variable which contains the number of
# projects.
array_vars_autocomplete() {
project_count=${#CROS_WORKON_PROJECT[@]}
# No project_count is really bad.
if [[ ${project_count} -eq 0 ]]; then
die "Must have at least one value in CROS_WORKON_PROJECT"
fi
# For one value, defaults will suffice, unless it's blank (likely undefined).
if [[ ${project_count} -eq 1 ]]; then
if [[ -z "${CROS_WORKON_PROJECT[@]}" ]]; then
die "Undefined CROS_WORKON_PROJECT"
fi
return
fi
local count var
for var in "${ARRAY_VARIABLES[@]}"; do
eval count=\${#${var}\[@\]}
if [[ ${count} -ne ${project_count} ]] && [[ ${count} -ne 1 ]]; then
die "${var} has ${count} projects. ${project_count} or one default expected."
fi
# Invariably, ${project_count} is at least 2 here. All variables also either
# have all items or the first serves as default (or isn't needed if
# empty). By looking at the second item, determine if we need to
# autocomplete.
local i
if [[ ${count} -ne ${project_count} ]]; then
for (( i = 1; i < project_count; ++i )); do
eval ${var}\[i\]=\${${var}\[0\]}
done
fi
eval einfo "${var}: \${${var}[@]}"
done
}
# Calculate path where code should be checked out.
# Result passed through global variable "path" to preserve proper array quoting.
get_paths() {
path=()
local i
for (( i = 0; i < project_count; ++i )); do
path+=( "${CROS_WORKON_REPO}/${CROS_WORKON_PROJECT[i]}" )
done
}
get_rev() {
GIT_DIR="$1" git rev-parse HEAD
}
cros-workon_src_unpack() {
# Sanity check. We cannot have S set to WORKDIR because if/when we try
# to check out repos, git will die if it tries to check out into a dir
# that already exists. Some packages might try this when out-of-tree
# builds are enabled, and they'll work fine most of the time because
# they'll be using a full manifest and will just re-use the existing
# checkout in src/platform/*. But if the code detects that it has to
# make its own checkout, things fall apart. For out-of-tree builds,
# the initial $S doesn't even matter because it resets it below to the
# repo in src/platform/.
if [[ ${S} == "${WORKDIR}" ]]; then
die "Sorry, but \$S cannot be set to \$WORKDIR"
fi
# Set the default of CROS_WORKON_DESTDIR. This is done here because S is
# sometimes overridden in ebuilds and we cannot rely on the global state
# (and therefore ordering of eclass inherits and local ebuild overrides).
: ${CROS_WORKON_DESTDIR:=${S}}
# Fix array variables
array_vars_autocomplete
# Make sure all CROS_WORKON_DESTDIR are under S.
local p
for p in "${CROS_WORKON_DESTDIR[@]}"; do
if [[ "${p}" != "${S}" && "${p}" != "${S}"/* ]]; then
die "CROS_WORKON_DESTDIR=${p} must be under S=${S}"
fi
done
local repo=( "${CROS_WORKON_REPO[@]}" )
local project=( "${CROS_WORKON_PROJECT[@]}" )
local destdir=( "${CROS_WORKON_DESTDIR[@]}" )
get_paths
all_local() {
local p
for p in "${path[@]}"; do
[[ -d ${p} ]] || return 1
done
return 0
}
local fetched=0
if all_local; then
for (( i = 0; i < project_count; ++i )); do
# Looks like we already have a local copy of all repositories.
# Let's use these and checkout ${CROS_WORKON_COMMIT}.
# -s: For speed, share objects between ${path} and ${S}.
# -n: Don't checkout any files from the repository yet. We'll
# checkout the source separately.
#
# We don't use git clone to checkout the source because the -b
# option for clone defaults to HEAD if it can't find the
# revision you requested. On the other hand, git checkout fails
# if it can't find the revision you requested, so we use that
# instead.
# Destination directory. If we have one project, it's simply
# ${CROS_WORKON_DESTDIR}. More projects either specify an array or go to
# ${S}/${project}.
if [[ "${CROS_WORKON_COMMIT[i]}" == "master" ]]; then
# Since we don't have a CROS_WORKON_COMMIT revision specified,
# we don't know what revision the ebuild wants. Let's take the
# version of the code that the user has checked out.
#
# This almost replicates the pre-cros-workon behavior, where
# the code you had in your source tree was used to build
# things. One difference here, however, is that only committed
# changes are included.
#
# TODO(davidjames): We should fix the preflight buildbot to
# specify CROS_WORKON_COMMIT for all ebuilds, and update this
# code path to fail and explain the problem.
git clone -s "${path[i]}" "${destdir[i]}" || \
die "Can't clone ${path[i]}."
: $(( ++fetched ))
else
git clone -sn "${path[i]}" "${destdir[i]}" || \
die "Can't clone ${path[i]}."
if ! ( cd ${destdir[i]} && git checkout -q ${CROS_WORKON_COMMIT[i]} ) ; then
ewarn "Cannot run git checkout ${CROS_WORKON_COMMIT[i]} in ${destdir[i]}."
ewarn "Is ${path[i]} up to date? Try running repo sync."
rm -rf "${destdir[i]}/.git"
else
: $(( ++fetched ))
fi
fi
done
if [[ ${fetched} -eq ${project_count} ]]; then
# TODO: Id of all repos?
# We should run get_rev in destdir[0] because CROS_WORKON_COMMIT
# is only checked out there. Also, we can't use
# CROS_WORKON_COMMIT directly because it could be a named or
# abbreviated ref.
cros-workon_enforce_subtrees
return
else
die "Could not checkout all projects."
fi
else
die "Not all projects are available locally."
fi
}
# Enforces subtree restrictions specified by CROS_WORKON_SUBTREE.
cros-workon_enforce_subtrees() {
local i j p q
local destdir=( "${CROS_WORKON_DESTDIR[@]}" )
# Gather the subtrees specified by CROS_WORKON_SUBTREE. All directories
# and files under those subtrees are not blacklisted.
local keep_dirs=()
for (( i = 0; i < project_count; ++i )); do
if [[ -z "${CROS_WORKON_SUBTREE[i]}" ]]; then
keep_dirs+=( "${destdir[i]}" )
else
for p in ${CROS_WORKON_SUBTREE[i]}; do
keep_dirs+=( "${destdir[i]}/${p}" )
done
fi
done
keep_dirs=( $(IFS=$'\n'; LC_ALL=C sort -u <<<"${keep_dirs[*]}") )
# Ignore overlapping subtrees.
for (( i = 0; i < ${#keep_dirs[@]}; ++i )); do
p="${keep_dirs[i]}"
: $(( j = i + 1 ))
while (( j < ${#keep_dirs[@]} )); do
q="${keep_dirs[j]}"
if [[ "${q}" == "${p}"/* ]]; then
einfo "Ignoring overlapping CROS_WORKON_SUBTREE: ${q} is under ${p}"
keep_dirs=( "${keep_dirs[@]:0:j}" "${keep_dirs[@]:$(( j + 1 ))}" )
else
: $(( ++j ))
fi
done
done
# If the directory to keep is $S only, then there is nothing we need to do.
if [[ "${#keep_dirs[@]}" == 1 && "${keep_dirs}" == "${S}" ]]; then
return
fi
# It is an error to specify a missing file in CROS_WORKON_SUBTREE.
for p in "${keep_dirs[@]}"; do
if [[ ! -e "${p}" ]]; then
die "File specified in CROS_WORKON_SUBTREE is missing: ${p}"
fi
done
# Gather the parent directories of subtrees to use.
# Those directories are exempted from blacklist because we need them to
# reach subtrees.
local keep_parents=()
for p in "${keep_dirs[@]}"; do
if [[ "${p}" == "${S}" ]]; then
continue
fi
q="${p%/*}"
while [[ "${q}" != "${S}" ]]; do
keep_parents+=( "${q}" )
q="${q%/*}"
done
done
keep_parents=( $(IFS=$'\n'; LC_ALL=C sort -u <<<"${keep_parents[*]}") )
# Construct arguments to pass to find(1) to list directories/files to
# blacklist.
#
# The command line built here is tricky, but it does the following
# during traversal of the filesystem by depth-first order:
#
# 1. Do nothing about the root directory ($S). Note that we should not
# reach here if there is nothing to blacklist.
# 2. If the visiting file is a parent directory of a subtree (i.e. in
# $keep_parents[@]), then recurse into its contents.
# 3. If the visiting file is the top directory of a subtree (i.e. in
# $keep_dirs[@]), then do not recurse into its contents.
# 4. Otherwise, blacklist the visiting file, and if it is a directory,
# do not recursive into its contents.
#
local find_args=( "${S}" -mindepth 1 )
for p in "${keep_parents[@]}"; do
find_args+=( ! -path "${p}" )
done
find_args+=( -prune )
for p in "${keep_dirs[@]}"; do
find_args+=( ! -path "${p}" )
done
if [[ "${S}" == "${WORKDIR}"/* ]]; then
# $S is writable, so just remove blacklisted files.
find "${find_args[@]}" -exec rm -rf {} +
else
# $S is read-only, so use portage sandbox.
local deny_paths="$(find "${find_args[@]}" -printf '%p:')"
deny_paths="${deny_paths%:}"
if [[ -n "${deny_paths}" ]]; then
adddeny "${deny_paths}"
fi
fi
}
cros-workon_pkg_info() {
print_quoted_array() { printf '"%s"\n' "$@"; }
array_vars_autocomplete > /dev/null
get_paths
CROS_WORKON_SRCDIR=("${path[@]}")
local val var
for var in CROS_WORKON_SRCDIR CROS_WORKON_PROJECT CROS_WORKON_COMMIT ; do
eval val=(\"\${${var}\[@\]}\")
echo ${var}=\($(print_quoted_array "${val[@]}")\)
done
}
EXPORT_FUNCTIONS src_unpack pkg_info
|