blob: 4ea0f268b062eb4a4d5fa27bd768348f1709cc0f [file] [log] [blame]
Colin Cross3f40fa42015-01-30 17:27:36 -08001// Copyright 2015 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 common
16
17import (
Colin Cross3f40fa42015-01-30 17:27:36 -080018 "fmt"
19 "reflect"
20 "runtime"
21 "strings"
Colin Crossf6566ed2015-03-24 11:13:38 -070022
23 "github.com/google/blueprint"
24 "github.com/google/blueprint/proptools"
Colin Cross3f40fa42015-01-30 17:27:36 -080025)
26
27var (
28 Arm = newArch32("Arm")
29 Arm64 = newArch64("Arm64")
30 Mips = newArch32("Mips")
31 Mips64 = newArch64("Mips64")
32 X86 = newArch32("X86")
33 X86_64 = newArch64("X86_64")
Colin Cross2fe66872015-03-30 17:20:39 -070034
35 Common = ArchType{
36 Name: "common",
37 }
Colin Cross3f40fa42015-01-30 17:27:36 -080038)
39
40/*
41Example blueprints file containing all variant property groups, with comment listing what type
42of variants get properties in that group:
43
44module {
45 arch: {
46 arm: {
47 // Host or device variants with arm architecture
48 },
49 arm64: {
50 // Host or device variants with arm64 architecture
51 },
52 mips: {
53 // Host or device variants with mips architecture
54 },
55 mips64: {
56 // Host or device variants with mips64 architecture
57 },
58 x86: {
59 // Host or device variants with x86 architecture
60 },
61 x86_64: {
62 // Host or device variants with x86_64 architecture
63 },
64 },
65 multilib: {
66 lib32: {
67 // Host or device variants for 32-bit architectures
68 },
69 lib64: {
70 // Host or device variants for 64-bit architectures
71 },
72 },
73 target: {
74 android: {
75 // Device variants
76 },
77 host: {
78 // Host variants
79 },
80 linux: {
81 // Linux host variants
82 },
83 darwin: {
84 // Darwin host variants
85 },
86 windows: {
87 // Windows host variants
88 },
89 not_windows: {
90 // Non-windows host variants
91 },
92 },
93}
94*/
95type archProperties struct {
96 Arch struct {
97 Arm interface{}
98 Arm64 interface{}
99 Mips interface{}
100 Mips64 interface{}
101 X86 interface{}
102 X86_64 interface{}
103 }
104 Multilib struct {
105 Lib32 interface{}
106 Lib64 interface{}
107 }
108 Target struct {
Colin Crossb05bff22015-04-30 15:08:04 -0700109 Host interface{}
110 Android interface{}
111 Android_arm interface{}
112 Android_arm64 interface{}
113 Android_mips interface{}
114 Android_mips64 interface{}
115 Android_x86 interface{}
116 Android_x86_64 interface{}
117 Android64 interface{}
118 Android32 interface{}
119 Linux interface{}
120 Linux_x86 interface{}
121 Linux_x86_64 interface{}
122 Darwin interface{}
123 Darwin_x86 interface{}
124 Darwin_x86_64 interface{}
125 Windows interface{}
126 Not_windows interface{}
Colin Cross3f40fa42015-01-30 17:27:36 -0800127 }
128}
129
130// An Arch indicates a single CPU architecture.
131type Arch struct {
Colin Crossd3ba0392015-05-07 14:11:29 -0700132 ArchType ArchType
133 ArchVariant string
134 CpuVariant string
135 Abi string
Colin Cross3f40fa42015-01-30 17:27:36 -0800136}
137
138func (a Arch) String() string {
Colin Crossd3ba0392015-05-07 14:11:29 -0700139 s := a.ArchType.String()
Colin Cross3f40fa42015-01-30 17:27:36 -0800140 if a.ArchVariant != "" {
141 s += "_" + a.ArchVariant
142 }
143 if a.CpuVariant != "" {
144 s += "_" + a.CpuVariant
145 }
146 return s
147}
148
149type ArchType struct {
150 Name string
151 Field string
152 Multilib string
153 MultilibField string
154}
155
156func newArch32(field string) ArchType {
157 return ArchType{
158 Name: strings.ToLower(field),
159 Field: field,
160 Multilib: "lib32",
161 MultilibField: "Lib32",
162 }
163}
164
165func newArch64(field string) ArchType {
166 return ArchType{
167 Name: strings.ToLower(field),
168 Field: field,
169 Multilib: "lib64",
170 MultilibField: "Lib64",
171 }
172}
173
174func (a ArchType) String() string {
175 return a.Name
176}
177
178type HostOrDeviceSupported int
179
180const (
181 _ HostOrDeviceSupported = iota
182 HostSupported
183 DeviceSupported
184 HostAndDeviceSupported
185)
186
187type HostOrDevice int
188
189const (
190 _ HostOrDevice = iota
191 Host
192 Device
193)
194
195func (hod HostOrDevice) String() string {
196 switch hod {
197 case Device:
198 return "device"
199 case Host:
200 return "host"
201 default:
202 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
203 }
204}
205
206func (hod HostOrDevice) FieldLower() string {
207 switch hod {
208 case Device:
209 return "android"
210 case Host:
211 return "host"
212 default:
213 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
214 }
215}
216
217func (hod HostOrDevice) Field() string {
218 switch hod {
219 case Device:
220 return "Android"
221 case Host:
222 return "Host"
223 default:
224 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
225 }
226}
227
228func (hod HostOrDevice) Host() bool {
229 if hod == 0 {
230 panic("HostOrDevice unset")
231 }
232 return hod == Host
233}
234
235func (hod HostOrDevice) Device() bool {
236 if hod == 0 {
237 panic("HostOrDevice unset")
238 }
239 return hod == Device
240}
241
242var hostOrDeviceName = map[HostOrDevice]string{
243 Device: "device",
244 Host: "host",
245}
246
247var (
248 armArch = Arch{
Colin Crossd3ba0392015-05-07 14:11:29 -0700249 ArchType: Arm,
250 ArchVariant: "armv7-a-neon",
251 CpuVariant: "cortex-a15",
252 Abi: "armeabi-v7a",
Colin Cross3f40fa42015-01-30 17:27:36 -0800253 }
254 arm64Arch = Arch{
Colin Crossd3ba0392015-05-07 14:11:29 -0700255 ArchType: Arm64,
256 ArchVariant: "armv8-a",
257 CpuVariant: "denver",
258 Abi: "arm64-v8a",
Colin Cross3f40fa42015-01-30 17:27:36 -0800259 }
Colin Crossd3ba0392015-05-07 14:11:29 -0700260 x86Arch = Arch{
261 ArchType: X86,
Colin Cross3f40fa42015-01-30 17:27:36 -0800262 }
Colin Crossd3ba0392015-05-07 14:11:29 -0700263 x8664Arch = Arch{
264 ArchType: X86_64,
Colin Cross3f40fa42015-01-30 17:27:36 -0800265 }
Colin Crossd3ba0392015-05-07 14:11:29 -0700266 commonArch = Arch{
267 ArchType: Common,
Colin Cross2fe66872015-03-30 17:20:39 -0700268 }
Colin Cross3f40fa42015-01-30 17:27:36 -0800269)
270
Colin Crossd3ba0392015-05-07 14:11:29 -0700271func HostOrDeviceMutator(mctx blueprint.EarlyMutatorContext) {
272 var module AndroidModule
273 var ok bool
274 if module, ok = mctx.Module().(AndroidModule); !ok {
275 return
276 }
277
278 hods := []HostOrDevice{}
279
280 if module.base().HostSupported() {
281 hods = append(hods, Host)
282 }
283
284 if module.base().DeviceSupported() {
285 hods = append(hods, Device)
286 }
287
288 if len(hods) == 0 {
289 return
290 }
291
292 hodNames := []string{}
293 for _, hod := range hods {
294 hodNames = append(hodNames, hod.String())
295 }
296
297 modules := mctx.CreateVariations(hodNames...)
298 for i, m := range modules {
299 m.(AndroidModule).base().SetHostOrDevice(hods[i])
300 }
301}
302
Colin Cross3f40fa42015-01-30 17:27:36 -0800303func ArchMutator(mctx blueprint.EarlyMutatorContext) {
304 var module AndroidModule
305 var ok bool
306 if module, ok = mctx.Module().(AndroidModule); !ok {
307 return
308 }
309
310 // TODO: this is all hardcoded for arm64 primary, arm secondary for now
311 // Replace with a configuration file written by lunch or bootstrap
312
313 arches := []Arch{}
314
Colin Crossd3ba0392015-05-07 14:11:29 -0700315 if module.base().HostSupported() && module.base().HostOrDevice().Host() {
Colin Cross2fe66872015-03-30 17:20:39 -0700316 switch module.base().commonProperties.Compile_multilib {
317 case "common":
Colin Crossd3ba0392015-05-07 14:11:29 -0700318 arches = append(arches, commonArch)
Colin Cross2fe66872015-03-30 17:20:39 -0700319 default:
Colin Crossd3ba0392015-05-07 14:11:29 -0700320 arches = append(arches, x8664Arch)
Colin Cross2fe66872015-03-30 17:20:39 -0700321 }
Colin Cross3f40fa42015-01-30 17:27:36 -0800322 }
323
Colin Crossd3ba0392015-05-07 14:11:29 -0700324 if module.base().DeviceSupported() && module.base().HostOrDevice().Device() {
Colin Cross3f40fa42015-01-30 17:27:36 -0800325 switch module.base().commonProperties.Compile_multilib {
Colin Cross2fe66872015-03-30 17:20:39 -0700326 case "common":
Colin Crossd3ba0392015-05-07 14:11:29 -0700327 arches = append(arches, commonArch)
Colin Cross3f40fa42015-01-30 17:27:36 -0800328 case "both":
329 arches = append(arches, arm64Arch, armArch)
330 case "first", "64":
331 arches = append(arches, arm64Arch)
332 case "32":
333 arches = append(arches, armArch)
334 default:
335 mctx.ModuleErrorf(`compile_multilib must be "both", "first", "32", or "64", found %q`,
336 module.base().commonProperties.Compile_multilib)
337 }
338 }
339
Colin Cross5049f022015-03-18 13:28:46 -0700340 if len(arches) == 0 {
341 return
342 }
343
Colin Cross3f40fa42015-01-30 17:27:36 -0800344 archNames := []string{}
345 for _, arch := range arches {
346 archNames = append(archNames, arch.String())
347 }
348
349 modules := mctx.CreateVariations(archNames...)
350
351 for i, m := range modules {
352 m.(AndroidModule).base().SetArch(arches[i])
Colin Crossd3ba0392015-05-07 14:11:29 -0700353 m.(AndroidModule).base().setArchProperties(mctx)
Colin Cross3f40fa42015-01-30 17:27:36 -0800354 }
355}
356
Colin Crossc472d572015-03-17 15:06:21 -0700357func InitArchModule(m AndroidModule, defaultMultilib Multilib,
Colin Cross3f40fa42015-01-30 17:27:36 -0800358 propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
359
360 base := m.base()
361
Colin Crossc472d572015-03-17 15:06:21 -0700362 base.commonProperties.Compile_multilib = string(defaultMultilib)
Colin Cross3f40fa42015-01-30 17:27:36 -0800363
364 base.generalProperties = append(base.generalProperties,
Colin Cross3f40fa42015-01-30 17:27:36 -0800365 propertyStructs...)
366
367 for _, properties := range base.generalProperties {
368 propertiesValue := reflect.ValueOf(properties)
369 if propertiesValue.Kind() != reflect.Ptr {
370 panic("properties must be a pointer to a struct")
371 }
372
373 propertiesValue = propertiesValue.Elem()
374 if propertiesValue.Kind() != reflect.Struct {
375 panic("properties must be a pointer to a struct")
376 }
377
378 archProperties := &archProperties{}
379 forEachInterface(reflect.ValueOf(archProperties), func(v reflect.Value) {
Colin Cross3ab7d882015-05-19 13:03:01 -0700380 newValue := proptools.CloneEmptyProperties(propertiesValue)
Colin Cross3f40fa42015-01-30 17:27:36 -0800381 v.Set(newValue)
382 })
383
384 base.archProperties = append(base.archProperties, archProperties)
385 }
386
387 var allProperties []interface{}
388 allProperties = append(allProperties, base.generalProperties...)
389 for _, asp := range base.archProperties {
390 allProperties = append(allProperties, asp)
391 }
392
393 return m, allProperties
394}
395
396// Rewrite the module's properties structs to contain arch-specific values.
Colin Crossd3ba0392015-05-07 14:11:29 -0700397func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext) {
398 arch := a.commonProperties.CompileArch
399 hod := a.commonProperties.CompileHostOrDevice
400
Colin Cross2fe66872015-03-30 17:20:39 -0700401 if arch.ArchType == Common {
402 return
403 }
404
Colin Cross3f40fa42015-01-30 17:27:36 -0800405 for i := range a.generalProperties {
406 generalPropsValue := reflect.ValueOf(a.generalProperties[i]).Elem()
407
408 // Handle arch-specific properties in the form:
Colin Crossb05bff22015-04-30 15:08:04 -0700409 // arch: {
410 // arm64: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800411 // key: value,
412 // },
413 // },
414 t := arch.ArchType
Colin Cross28d76592015-03-26 16:14:04 -0700415 a.extendProperties(ctx, "arch", t.Name, generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800416 reflect.ValueOf(a.archProperties[i].Arch).FieldByName(t.Field).Elem().Elem())
417
418 // Handle multilib-specific properties in the form:
Colin Crossb05bff22015-04-30 15:08:04 -0700419 // multilib: {
420 // lib32: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800421 // key: value,
422 // },
423 // },
Colin Cross28d76592015-03-26 16:14:04 -0700424 a.extendProperties(ctx, "multilib", t.Multilib, generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800425 reflect.ValueOf(a.archProperties[i].Multilib).FieldByName(t.MultilibField).Elem().Elem())
426
427 // Handle host-or-device-specific properties in the form:
Colin Crossb05bff22015-04-30 15:08:04 -0700428 // target: {
429 // host: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800430 // key: value,
431 // },
432 // },
Colin Cross28d76592015-03-26 16:14:04 -0700433 a.extendProperties(ctx, "target", hod.FieldLower(), generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800434 reflect.ValueOf(a.archProperties[i].Target).FieldByName(hod.Field()).Elem().Elem())
435
436 // Handle host target properties in the form:
Colin Crossb05bff22015-04-30 15:08:04 -0700437 // target: {
438 // linux: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800439 // key: value,
440 // },
Colin Crossb05bff22015-04-30 15:08:04 -0700441 // not_windows: {
442 // key: value,
443 // },
444 // linux_x86: {
445 // key: value,
446 // },
447 // linux_arm: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800448 // key: value,
449 // },
450 // },
451 var osList = []struct {
452 goos string
453 field string
454 }{
455 {"darwin", "Darwin"},
456 {"linux", "Linux"},
457 {"windows", "Windows"},
458 }
459
460 if hod.Host() {
461 for _, v := range osList {
462 if v.goos == runtime.GOOS {
Colin Cross28d76592015-03-26 16:14:04 -0700463 a.extendProperties(ctx, "target", v.goos, generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800464 reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field).Elem().Elem())
Colin Crossb05bff22015-04-30 15:08:04 -0700465 t := arch.ArchType
466 a.extendProperties(ctx, "target", v.goos+"_"+t.Name, generalPropsValue,
467 reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field+"_"+t.Name).Elem().Elem())
Colin Cross3f40fa42015-01-30 17:27:36 -0800468 }
469 }
Colin Cross28d76592015-03-26 16:14:04 -0700470 a.extendProperties(ctx, "target", "not_windows", generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800471 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Not_windows").Elem().Elem())
472 }
473
Colin Crossf8209412015-03-26 14:44:26 -0700474 // Handle 64-bit device properties in the form:
475 // target {
476 // android64 {
477 // key: value,
478 // },
479 // android32 {
480 // key: value,
481 // },
482 // },
483 // WARNING: this is probably not what you want to use in your blueprints file, it selects
484 // options for all targets on a device that supports 64-bit binaries, not just the targets
485 // that are being compiled for 64-bit. Its expected use case is binaries like linker and
486 // debuggerd that need to know when they are a 32-bit process running on a 64-bit device
487 if hod.Device() {
488 if true /* && target_is_64_bit */ {
Colin Cross28d76592015-03-26 16:14:04 -0700489 a.extendProperties(ctx, "target", "android64", generalPropsValue,
Colin Crossf8209412015-03-26 14:44:26 -0700490 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android64").Elem().Elem())
491 } else {
Colin Cross28d76592015-03-26 16:14:04 -0700492 a.extendProperties(ctx, "target", "android32", generalPropsValue,
Colin Crossf8209412015-03-26 14:44:26 -0700493 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android32").Elem().Elem())
494 }
495 }
Colin Crossb05bff22015-04-30 15:08:04 -0700496
497 // Handle device architecture properties in the form:
498 // target {
499 // android_arm {
500 // key: value,
501 // },
502 // android_x86 {
503 // key: value,
504 // },
505 // },
506 if hod.Device() {
507 t := arch.ArchType
508 a.extendProperties(ctx, "target", "android_"+t.Name, generalPropsValue,
509 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android_"+t.Name).Elem().Elem())
510 }
511
Colin Cross3f40fa42015-01-30 17:27:36 -0800512 if ctx.Failed() {
513 return
514 }
515 }
516}
517
518func forEachInterface(v reflect.Value, f func(reflect.Value)) {
519 switch v.Kind() {
520 case reflect.Interface:
521 f(v)
522 case reflect.Struct:
523 for i := 0; i < v.NumField(); i++ {
524 forEachInterface(v.Field(i), f)
525 }
526 case reflect.Ptr:
527 forEachInterface(v.Elem(), f)
528 default:
529 panic(fmt.Errorf("Unsupported kind %s", v.Kind()))
530 }
531}
532
533// TODO: move this to proptools
Colin Cross28d76592015-03-26 16:14:04 -0700534func (a *AndroidModuleBase) extendProperties(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
Colin Cross3f40fa42015-01-30 17:27:36 -0800535 dstValue, srcValue reflect.Value) {
Colin Cross28d76592015-03-26 16:14:04 -0700536 a.extendPropertiesRecursive(ctx, variationType, variationName, dstValue, srcValue, "")
Colin Cross3f40fa42015-01-30 17:27:36 -0800537}
538
Colin Cross28d76592015-03-26 16:14:04 -0700539func (a *AndroidModuleBase) extendPropertiesRecursive(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
Colin Cross3f40fa42015-01-30 17:27:36 -0800540 dstValue, srcValue reflect.Value, recursePrefix string) {
541
542 typ := dstValue.Type()
543 if srcValue.Type() != typ {
544 panic(fmt.Errorf("can't extend mismatching types (%s <- %s)",
545 dstValue.Kind(), srcValue.Kind()))
546 }
547
548 for i := 0; i < srcValue.NumField(); i++ {
549 field := typ.Field(i)
550 if field.PkgPath != "" {
551 // The field is not exported so just skip it.
552 continue
553 }
554
555 srcFieldValue := srcValue.Field(i)
556 dstFieldValue := dstValue.Field(i)
557
558 localPropertyName := proptools.PropertyNameForField(field.Name)
559 propertyName := fmt.Sprintf("%s.%s.%s%s", variationType, variationName,
560 recursePrefix, localPropertyName)
561 propertyPresentInVariation := ctx.ContainsProperty(propertyName)
562
563 if !propertyPresentInVariation {
564 continue
565 }
566
567 tag := field.Tag.Get("android")
568 tags := map[string]bool{}
569 for _, entry := range strings.Split(tag, ",") {
570 if entry != "" {
571 tags[entry] = true
572 }
573 }
574
575 if !tags["arch_variant"] {
576 ctx.PropertyErrorf(propertyName, "property %q can't be specific to a build variant",
577 recursePrefix+proptools.PropertyNameForField(field.Name))
578 continue
579 }
580
Colin Cross28d76592015-03-26 16:14:04 -0700581 if !ctx.ContainsProperty(propertyName) {
582 continue
583 }
584 a.extendedProperties[localPropertyName] = struct{}{}
585
Colin Cross3f40fa42015-01-30 17:27:36 -0800586 switch srcFieldValue.Kind() {
587 case reflect.Bool:
588 // Replace the original value.
589 dstFieldValue.Set(srcFieldValue)
590 case reflect.String:
591 // Append the extension string.
592 dstFieldValue.SetString(dstFieldValue.String() +
593 srcFieldValue.String())
594 case reflect.Struct:
595 // Recursively extend the struct's fields.
596 newRecursePrefix := fmt.Sprintf("%s%s.", recursePrefix, strings.ToLower(field.Name))
Colin Cross28d76592015-03-26 16:14:04 -0700597 a.extendPropertiesRecursive(ctx, variationType, variationName,
Colin Cross3f40fa42015-01-30 17:27:36 -0800598 dstFieldValue, srcFieldValue,
599 newRecursePrefix)
600 case reflect.Slice:
601 val, err := archCombineSlices(dstFieldValue, srcFieldValue, tags["arch_subtract"])
602 if err != nil {
603 ctx.PropertyErrorf(propertyName, err.Error())
604 continue
605 }
606 dstFieldValue.Set(val)
607 case reflect.Ptr, reflect.Interface:
608 // Recursively extend the pointed-to struct's fields.
609 if dstFieldValue.IsNil() != srcFieldValue.IsNil() {
610 panic(fmt.Errorf("can't extend field %q: nilitude mismatch"))
611 }
612 if dstFieldValue.Type() != srcFieldValue.Type() {
613 panic(fmt.Errorf("can't extend field %q: type mismatch"))
614 }
615 if !dstFieldValue.IsNil() {
616 newRecursePrefix := fmt.Sprintf("%s.%s", recursePrefix, field.Name)
Colin Cross28d76592015-03-26 16:14:04 -0700617 a.extendPropertiesRecursive(ctx, variationType, variationName,
Colin Cross3f40fa42015-01-30 17:27:36 -0800618 dstFieldValue.Elem(), srcFieldValue.Elem(),
619 newRecursePrefix)
620 }
621 default:
622 panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
623 field.Name, srcFieldValue.Kind()))
624 }
625 }
626}
627
628func archCombineSlices(general, arch reflect.Value, canSubtract bool) (reflect.Value, error) {
629 if !canSubtract {
630 // Append the extension slice.
631 return reflect.AppendSlice(general, arch), nil
632 }
633
634 // Support -val in arch list to subtract a value from original list
635 l := general.Interface().([]string)
636 for archIndex := 0; archIndex < arch.Len(); archIndex++ {
637 archString := arch.Index(archIndex).String()
638 if strings.HasPrefix(archString, "-") {
639 generalIndex := findStringInSlice(archString[1:], l)
640 if generalIndex == -1 {
641 return reflect.Value{},
642 fmt.Errorf("can't find %q to subtract from general properties", archString[1:])
643 }
644 l = append(l[:generalIndex], l[generalIndex+1:]...)
645 } else {
646 l = append(l, archString)
647 }
648 }
649
650 return reflect.ValueOf(l), nil
651}
652
653func findStringInSlice(str string, slice []string) int {
654 for i, s := range slice {
655 if s == str {
656 return i
657 }
658 }
659
660 return -1
661}