blob: 48d958972584f3bb021523cf2883d532c9d99c73 [file] [log] [blame]
Jingwen Chen30f5aaa2020-11-19 05:38:02 -05001// Copyright 2020 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package bazel
16
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000017import (
18 "fmt"
Jingwen Chen63930982021-03-24 10:04:33 -040019 "path/filepath"
Liz Kammera060c452021-03-24 10:14:47 -040020 "regexp"
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +000021 "sort"
22)
Jingwen Chen5d864492021-02-24 07:20:12 -050023
Jingwen Chen73850672020-12-14 08:25:34 -050024// BazelTargetModuleProperties contain properties and metadata used for
25// Blueprint to BUILD file conversion.
26type BazelTargetModuleProperties struct {
27 // The Bazel rule class for this target.
Liz Kammerfc46bc12021-02-19 11:06:17 -050028 Rule_class string `blueprint:"mutated"`
Jingwen Chen40067de2021-01-26 21:58:43 -050029
30 // The target label for the bzl file containing the definition of the rule class.
Liz Kammerfc46bc12021-02-19 11:06:17 -050031 Bzl_load_location string `blueprint:"mutated"`
Jingwen Chen73850672020-12-14 08:25:34 -050032}
Liz Kammer356f7d42021-01-26 09:18:53 -050033
Jingwen Chenfb4692a2021-02-07 10:05:16 -050034const BazelTargetModuleNamePrefix = "__bp2build__"
35
Liz Kammera060c452021-03-24 10:14:47 -040036var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
37
Jingwen Chen38e62642021-04-19 05:00:15 +000038// Label is used to represent a Bazel compatible Label. Also stores the original
39// bp text to support string replacement.
Liz Kammer356f7d42021-01-26 09:18:53 -050040type Label struct {
Jingwen Chen38e62642021-04-19 05:00:15 +000041 // The string representation of a Bazel target label. This can be a relative
42 // or fully qualified label. These labels are used for generating BUILD
43 // files with bp2build.
44 Label string
45
46 // The original Soong/Blueprint module name that the label was derived from.
47 // This is used for replacing references to the original name with the new
48 // label, for example in genrule cmds.
49 //
50 // While there is a reversible 1:1 mapping from the module name to Bazel
51 // label with bp2build that could make computing the original module name
52 // from the label automatic, it is not the case for handcrafted targets,
53 // where modules can have a custom label mapping through the { bazel_module:
54 // { label: <label> } } property.
55 //
56 // With handcrafted labels, those modules don't go through bp2build
57 // conversion, but relies on handcrafted targets in the source tree.
58 OriginalModuleName string
Liz Kammer356f7d42021-01-26 09:18:53 -050059}
60
61// LabelList is used to represent a list of Bazel labels.
62type LabelList struct {
63 Includes []Label
64 Excludes []Label
65}
66
Jingwen Chen63930982021-03-24 10:04:33 -040067// GlobsInDir returns a list of glob expressions for a list of extensions
68// (optionally recursive) within a directory.
69func GlobsInDir(dir string, recursive bool, extensions []string) []string {
70 globs := []string{}
71
72 globInfix := ""
73 if dir == "." {
74 if recursive {
75 // e.g "**/*.h"
76 globInfix = "**/"
77 } // else e.g. "*.h"
78 for _, ext := range extensions {
79 globs = append(globs, globInfix+"*"+ext)
80 }
81 } else {
82 if recursive {
83 // e.g. "foo/bar/**/*.h"
84 dir += "/**"
85 } // else e.g. "foo/bar/*.h"
86 for _, ext := range extensions {
87 globs = append(globs, dir+"/*"+ext)
88 }
89 }
90 return globs
91}
92
93// LooseHdrsGlobs returns the list of non-recursive header globs for each parent directory of
94// each source file in this LabelList's Includes.
95func (ll *LabelList) LooseHdrsGlobs(exts []string) []string {
96 var globs []string
97 for _, parentDir := range ll.uniqueParentDirectories() {
98 globs = append(globs,
99 GlobsInDir(parentDir, false, exts)...)
100 }
101 return globs
102}
103
104// uniqueParentDirectories returns a list of the unique parent directories for
105// all files in ll.Includes.
106func (ll *LabelList) uniqueParentDirectories() []string {
107 dirMap := map[string]bool{}
108 for _, label := range ll.Includes {
109 dirMap[filepath.Dir(label.Label)] = true
110 }
111 dirs := []string{}
112 for dir := range dirMap {
113 dirs = append(dirs, dir)
114 }
115 return dirs
116}
117
Liz Kammer356f7d42021-01-26 09:18:53 -0500118// Append appends the fields of other labelList to the corresponding fields of ll.
119func (ll *LabelList) Append(other LabelList) {
120 if len(ll.Includes) > 0 || len(other.Includes) > 0 {
121 ll.Includes = append(ll.Includes, other.Includes...)
122 }
123 if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
124 ll.Excludes = append(other.Excludes, other.Excludes...)
125 }
126}
Jingwen Chen5d864492021-02-24 07:20:12 -0500127
Jingwen Chened9c17d2021-04-13 07:14:55 +0000128// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
129// the slice in a sorted order.
130func UniqueSortedBazelLabels(originalLabels []Label) []Label {
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000131 uniqueLabelsSet := make(map[Label]bool)
132 for _, l := range originalLabels {
133 uniqueLabelsSet[l] = true
134 }
135 var uniqueLabels []Label
136 for l, _ := range uniqueLabelsSet {
137 uniqueLabels = append(uniqueLabels, l)
138 }
139 sort.SliceStable(uniqueLabels, func(i, j int) bool {
140 return uniqueLabels[i].Label < uniqueLabels[j].Label
141 })
142 return uniqueLabels
143}
144
145func UniqueBazelLabelList(originalLabelList LabelList) LabelList {
146 var uniqueLabelList LabelList
Jingwen Chened9c17d2021-04-13 07:14:55 +0000147 uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
148 uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
Rupert Shuttleworth2e4219b2021-03-12 11:04:21 +0000149 return uniqueLabelList
150}
151
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000152// Subtract needle from haystack
153func SubtractStrings(haystack []string, needle []string) []string {
154 // This is really a set
155 remainder := make(map[string]bool)
156
157 for _, s := range haystack {
158 remainder[s] = true
159 }
160 for _, s := range needle {
161 delete(remainder, s)
162 }
163
164 var strings []string
165 for s, _ := range remainder {
166 strings = append(strings, s)
167 }
168
169 sort.SliceStable(strings, func(i, j int) bool {
170 return strings[i] < strings[j]
171 })
172
173 return strings
174}
175
176// Subtract needle from haystack
177func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
178 // This is really a set
179 remainder := make(map[Label]bool)
180
181 for _, label := range haystack {
182 remainder[label] = true
183 }
184 for _, label := range needle {
185 delete(remainder, label)
186 }
187
188 var labels []Label
189 for label, _ := range remainder {
190 labels = append(labels, label)
191 }
192
193 sort.SliceStable(labels, func(i, j int) bool {
194 return labels[i].Label < labels[j].Label
195 })
196
197 return labels
198}
199
200// Subtract needle from haystack
201func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
202 var result LabelList
203 result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
204 // NOTE: Excludes are intentionally not subtracted
205 result.Excludes = haystack.Excludes
206 return result
207}
208
Jingwen Chen07027912021-03-15 06:02:43 -0400209const (
Jingwen Chen91220d72021-03-24 02:18:33 -0400210 // ArchType names in arch.go
Jingwen Chen07027912021-03-15 06:02:43 -0400211 ARCH_ARM = "arm"
212 ARCH_ARM64 = "arm64"
Jingwen Chen91220d72021-03-24 02:18:33 -0400213 ARCH_X86 = "x86"
214 ARCH_X86_64 = "x86_64"
215
216 // OsType names in arch.go
217 OS_ANDROID = "android"
218 OS_DARWIN = "darwin"
219 OS_FUCHSIA = "fuchsia"
220 OS_LINUX = "linux_glibc"
221 OS_LINUX_BIONIC = "linux_bionic"
222 OS_WINDOWS = "windows"
Jingwen Chen07027912021-03-15 06:02:43 -0400223)
224
225var (
Jingwen Chenc1c26502021-04-05 10:35:13 +0000226 // These are the list of OSes and architectures with a Bazel config_setting
227 // and constraint value equivalent. These exist in arch.go, but the android
228 // package depends on the bazel package, so a cyclic dependency prevents
229 // using those variables here.
Jingwen Chen91220d72021-03-24 02:18:33 -0400230
231 // A map of architectures to the Bazel label of the constraint_value
232 // for the @platforms//cpu:cpu constraint_setting
233 PlatformArchMap = map[string]string{
234 ARCH_ARM: "//build/bazel/platforms/arch:arm",
235 ARCH_ARM64: "//build/bazel/platforms/arch:arm64",
236 ARCH_X86: "//build/bazel/platforms/arch:x86",
237 ARCH_X86_64: "//build/bazel/platforms/arch:x86_64",
238 }
239
240 // A map of target operating systems to the Bazel label of the
241 // constraint_value for the @platforms//os:os constraint_setting
242 PlatformOsMap = map[string]string{
243 OS_ANDROID: "//build/bazel/platforms/os:android",
244 OS_DARWIN: "//build/bazel/platforms/os:darwin",
245 OS_FUCHSIA: "//build/bazel/platforms/os:fuchsia",
246 OS_LINUX: "//build/bazel/platforms/os:linux",
247 OS_LINUX_BIONIC: "//build/bazel/platforms/os:linux_bionic",
248 OS_WINDOWS: "//build/bazel/platforms/os:windows",
249 }
Jingwen Chen07027912021-03-15 06:02:43 -0400250)
251
Jingwen Chenc1c26502021-04-05 10:35:13 +0000252type Attribute interface {
253 HasConfigurableValues() bool
254}
255
Jingwen Chen07027912021-03-15 06:02:43 -0400256// Arch-specific label_list typed Bazel attribute values. This should correspond
257// to the types of architectures supported for compilation in arch.go.
258type labelListArchValues struct {
259 X86 LabelList
260 X86_64 LabelList
261 Arm LabelList
262 Arm64 LabelList
Jingwen Chen91220d72021-03-24 02:18:33 -0400263 Common LabelList
264}
265
266type labelListOsValues struct {
267 Android LabelList
268 Darwin LabelList
269 Fuchsia LabelList
270 Linux LabelList
271 LinuxBionic LabelList
272 Windows LabelList
Jingwen Chen07027912021-03-15 06:02:43 -0400273}
274
275// LabelListAttribute is used to represent a list of Bazel labels as an
276// attribute.
277type LabelListAttribute struct {
278 // The non-arch specific attribute label list Value. Required.
279 Value LabelList
280
281 // The arch-specific attribute label list values. Optional. If used, these
282 // are generated in a select statement and appended to the non-arch specific
283 // label list Value.
284 ArchValues labelListArchValues
Jingwen Chen91220d72021-03-24 02:18:33 -0400285
286 // The os-specific attribute label list values. Optional. If used, these
287 // are generated in a select statement and appended to the non-os specific
288 // label list Value.
289 OsValues labelListOsValues
Jingwen Chen07027912021-03-15 06:02:43 -0400290}
291
292// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
293func MakeLabelListAttribute(value LabelList) LabelListAttribute {
294 return LabelListAttribute{Value: UniqueBazelLabelList(value)}
295}
296
Jingwen Chened9c17d2021-04-13 07:14:55 +0000297// Append all values, including os and arch specific ones, from another
Jingwen Chen63930982021-03-24 10:04:33 -0400298// LabelListAttribute to this LabelListAttribute.
299func (attrs *LabelListAttribute) Append(other LabelListAttribute) {
300 for arch := range PlatformArchMap {
301 this := attrs.GetValueForArch(arch)
302 that := other.GetValueForArch(arch)
303 this.Append(that)
304 attrs.SetValueForArch(arch, this)
305 }
306
307 for os := range PlatformOsMap {
308 this := attrs.GetValueForOS(os)
309 that := other.GetValueForOS(os)
310 this.Append(that)
311 attrs.SetValueForOS(os, this)
312 }
313
314 attrs.Value.Append(other.Value)
315}
316
Jingwen Chen07027912021-03-15 06:02:43 -0400317// HasArchSpecificValues returns true if the attribute contains
318// architecture-specific label_list values.
Jingwen Chenc1c26502021-04-05 10:35:13 +0000319func (attrs LabelListAttribute) HasConfigurableValues() bool {
320 for arch := range PlatformArchMap {
Jingwen Chen91220d72021-03-24 02:18:33 -0400321 if len(attrs.GetValueForArch(arch).Includes) > 0 {
322 return true
323 }
324 }
325
Jingwen Chenc1c26502021-04-05 10:35:13 +0000326 for os := range PlatformOsMap {
Jingwen Chen91220d72021-03-24 02:18:33 -0400327 if len(attrs.GetValueForOS(os).Includes) > 0 {
Jingwen Chen07027912021-03-15 06:02:43 -0400328 return true
329 }
330 }
331 return false
332}
333
Jingwen Chen91220d72021-03-24 02:18:33 -0400334func (attrs *LabelListAttribute) archValuePtrs() map[string]*LabelList {
335 return map[string]*LabelList{
336 ARCH_X86: &attrs.ArchValues.X86,
337 ARCH_X86_64: &attrs.ArchValues.X86_64,
338 ARCH_ARM: &attrs.ArchValues.Arm,
339 ARCH_ARM64: &attrs.ArchValues.Arm64,
340 }
341}
342
Jingwen Chen07027912021-03-15 06:02:43 -0400343// GetValueForArch returns the label_list attribute value for an architecture.
344func (attrs *LabelListAttribute) GetValueForArch(arch string) LabelList {
Jingwen Chen91220d72021-03-24 02:18:33 -0400345 var v *LabelList
346 if v = attrs.archValuePtrs()[arch]; v == nil {
Jingwen Chen07027912021-03-15 06:02:43 -0400347 panic(fmt.Errorf("Unknown arch: %s", arch))
348 }
Jingwen Chen91220d72021-03-24 02:18:33 -0400349 return *v
Jingwen Chen07027912021-03-15 06:02:43 -0400350}
351
352// SetValueForArch sets the label_list attribute value for an architecture.
353func (attrs *LabelListAttribute) SetValueForArch(arch string, value LabelList) {
Jingwen Chen91220d72021-03-24 02:18:33 -0400354 var v *LabelList
355 if v = attrs.archValuePtrs()[arch]; v == nil {
Jingwen Chen07027912021-03-15 06:02:43 -0400356 panic(fmt.Errorf("Unknown arch: %s", arch))
357 }
Jingwen Chen91220d72021-03-24 02:18:33 -0400358 *v = value
359}
360
361func (attrs *LabelListAttribute) osValuePtrs() map[string]*LabelList {
362 return map[string]*LabelList{
363 OS_ANDROID: &attrs.OsValues.Android,
364 OS_DARWIN: &attrs.OsValues.Darwin,
365 OS_FUCHSIA: &attrs.OsValues.Fuchsia,
366 OS_LINUX: &attrs.OsValues.Linux,
367 OS_LINUX_BIONIC: &attrs.OsValues.LinuxBionic,
368 OS_WINDOWS: &attrs.OsValues.Windows,
369 }
370}
371
372// GetValueForOS returns the label_list attribute value for an OS target.
373func (attrs *LabelListAttribute) GetValueForOS(os string) LabelList {
374 var v *LabelList
375 if v = attrs.osValuePtrs()[os]; v == nil {
376 panic(fmt.Errorf("Unknown os: %s", os))
377 }
378 return *v
379}
380
381// SetValueForArch sets the label_list attribute value for an OS target.
382func (attrs *LabelListAttribute) SetValueForOS(os string, value LabelList) {
383 var v *LabelList
384 if v = attrs.osValuePtrs()[os]; v == nil {
385 panic(fmt.Errorf("Unknown os: %s", os))
386 }
387 *v = value
Jingwen Chen07027912021-03-15 06:02:43 -0400388}
389
Jingwen Chen5d864492021-02-24 07:20:12 -0500390// StringListAttribute corresponds to the string_list Bazel attribute type with
391// support for additional metadata, like configurations.
392type StringListAttribute struct {
393 // The base value of the string list attribute.
394 Value []string
395
Jingwen Chenc1c26502021-04-05 10:35:13 +0000396 // The arch-specific attribute string list values. Optional. If used, these
397 // are generated in a select statement and appended to the non-arch specific
398 // label list Value.
Jingwen Chen5d864492021-02-24 07:20:12 -0500399 ArchValues stringListArchValues
Jingwen Chenc1c26502021-04-05 10:35:13 +0000400
401 // The os-specific attribute string list values. Optional. If used, these
402 // are generated in a select statement and appended to the non-os specific
403 // label list Value.
404 OsValues stringListOsValues
Jingwen Chen5d864492021-02-24 07:20:12 -0500405}
406
Rupert Shuttleworthb8151682021-04-06 20:06:21 +0000407// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
408func MakeStringListAttribute(value []string) StringListAttribute {
409 // NOTE: These strings are not necessarily unique or sorted.
410 return StringListAttribute{Value: value}
411}
412
Jingwen Chen5d864492021-02-24 07:20:12 -0500413// Arch-specific string_list typed Bazel attribute values. This should correspond
414// to the types of architectures supported for compilation in arch.go.
415type stringListArchValues struct {
Jingwen Chen07027912021-03-15 06:02:43 -0400416 X86 []string
417 X86_64 []string
418 Arm []string
419 Arm64 []string
Jingwen Chen91220d72021-03-24 02:18:33 -0400420 Common []string
Jingwen Chen5d864492021-02-24 07:20:12 -0500421}
422
Jingwen Chenc1c26502021-04-05 10:35:13 +0000423type stringListOsValues struct {
424 Android []string
425 Darwin []string
426 Fuchsia []string
427 Linux []string
428 LinuxBionic []string
429 Windows []string
430}
431
Jingwen Chen91220d72021-03-24 02:18:33 -0400432// HasConfigurableValues returns true if the attribute contains
Jingwen Chen5d864492021-02-24 07:20:12 -0500433// architecture-specific string_list values.
Jingwen Chenc1c26502021-04-05 10:35:13 +0000434func (attrs StringListAttribute) HasConfigurableValues() bool {
435 for arch := range PlatformArchMap {
Jingwen Chen5d864492021-02-24 07:20:12 -0500436 if len(attrs.GetValueForArch(arch)) > 0 {
437 return true
438 }
439 }
Jingwen Chenc1c26502021-04-05 10:35:13 +0000440
441 for os := range PlatformOsMap {
442 if len(attrs.GetValueForOS(os)) > 0 {
443 return true
444 }
445 }
Jingwen Chen5d864492021-02-24 07:20:12 -0500446 return false
447}
448
Jingwen Chen91220d72021-03-24 02:18:33 -0400449func (attrs *StringListAttribute) archValuePtrs() map[string]*[]string {
450 return map[string]*[]string{
451 ARCH_X86: &attrs.ArchValues.X86,
452 ARCH_X86_64: &attrs.ArchValues.X86_64,
453 ARCH_ARM: &attrs.ArchValues.Arm,
454 ARCH_ARM64: &attrs.ArchValues.Arm64,
455 }
456}
457
Jingwen Chen5d864492021-02-24 07:20:12 -0500458// GetValueForArch returns the string_list attribute value for an architecture.
459func (attrs *StringListAttribute) GetValueForArch(arch string) []string {
Jingwen Chen91220d72021-03-24 02:18:33 -0400460 var v *[]string
461 if v = attrs.archValuePtrs()[arch]; v == nil {
Jingwen Chen5d864492021-02-24 07:20:12 -0500462 panic(fmt.Errorf("Unknown arch: %s", arch))
463 }
Jingwen Chen91220d72021-03-24 02:18:33 -0400464 return *v
Jingwen Chen5d864492021-02-24 07:20:12 -0500465}
466
467// SetValueForArch sets the string_list attribute value for an architecture.
468func (attrs *StringListAttribute) SetValueForArch(arch string, value []string) {
Jingwen Chen91220d72021-03-24 02:18:33 -0400469 var v *[]string
470 if v = attrs.archValuePtrs()[arch]; v == nil {
Jingwen Chen5d864492021-02-24 07:20:12 -0500471 panic(fmt.Errorf("Unknown arch: %s", arch))
472 }
Jingwen Chen91220d72021-03-24 02:18:33 -0400473 *v = value
Jingwen Chen5d864492021-02-24 07:20:12 -0500474}
Liz Kammera060c452021-03-24 10:14:47 -0400475
Jingwen Chenc1c26502021-04-05 10:35:13 +0000476func (attrs *StringListAttribute) osValuePtrs() map[string]*[]string {
477 return map[string]*[]string{
478 OS_ANDROID: &attrs.OsValues.Android,
479 OS_DARWIN: &attrs.OsValues.Darwin,
480 OS_FUCHSIA: &attrs.OsValues.Fuchsia,
481 OS_LINUX: &attrs.OsValues.Linux,
482 OS_LINUX_BIONIC: &attrs.OsValues.LinuxBionic,
483 OS_WINDOWS: &attrs.OsValues.Windows,
484 }
485}
486
487// GetValueForOS returns the string_list attribute value for an OS target.
488func (attrs *StringListAttribute) GetValueForOS(os string) []string {
489 var v *[]string
490 if v = attrs.osValuePtrs()[os]; v == nil {
491 panic(fmt.Errorf("Unknown os: %s", os))
492 }
493 return *v
494}
495
496// SetValueForArch sets the string_list attribute value for an OS target.
497func (attrs *StringListAttribute) SetValueForOS(os string, value []string) {
498 var v *[]string
499 if v = attrs.osValuePtrs()[os]; v == nil {
500 panic(fmt.Errorf("Unknown os: %s", os))
501 }
502 *v = value
503}
504
Jingwen Chened9c17d2021-04-13 07:14:55 +0000505// Append appends all values, including os and arch specific ones, from another
506// StringListAttribute to this StringListAttribute
507func (attrs *StringListAttribute) Append(other StringListAttribute) {
508 for arch := range PlatformArchMap {
509 this := attrs.GetValueForArch(arch)
510 that := other.GetValueForArch(arch)
511 this = append(this, that...)
512 attrs.SetValueForArch(arch, this)
513 }
514
515 for os := range PlatformOsMap {
516 this := attrs.GetValueForOS(os)
517 that := other.GetValueForOS(os)
518 this = append(this, that...)
519 attrs.SetValueForOS(os, this)
520 }
521
522 attrs.Value = append(attrs.Value, other.Value...)
523}
524
Liz Kammera060c452021-03-24 10:14:47 -0400525// TryVariableSubstitution, replace string substitution formatting within each string in slice with
526// Starlark string.format compatible tag for productVariable.
527func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
528 ret := make([]string, 0, len(slice))
529 changesMade := false
530 for _, s := range slice {
531 newS, changed := TryVariableSubstitution(s, productVariable)
532 ret = append(ret, newS)
533 changesMade = changesMade || changed
534 }
535 return ret, changesMade
536}
537
538// TryVariableSubstitution, replace string substitution formatting within s with Starlark
539// string.format compatible tag for productVariable.
540func TryVariableSubstitution(s string, productVariable string) (string, bool) {
541 sub := productVariableSubstitutionPattern.ReplaceAllString(s, "{"+productVariable+"}")
542 return sub, s != sub
543}