1// Copyright 2017 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 15// This file implements the logic of bpfix and also provides a programmatic interface 16 17package bpfix 18 19import ( 20 "bytes" 21 "errors" 22 "fmt" 23 "io" 24 "path/filepath" 25 "strings" 26 27 "github.com/google/blueprint/parser" 28) 29 30// Reformat takes a blueprint file as a string and returns a formatted version 31func Reformat(input string) (string, error) { 32 tree, err := parse("<string>", bytes.NewBufferString(input)) 33 if err != nil { 34 return "", err 35 } 36 37 res, err := parser.Print(tree) 38 if err != nil { 39 return "", err 40 } 41 42 return string(res), nil 43} 44 45// A FixRequest specifies the details of which fixes to apply to an individual file 46// A FixRequest doesn't specify whether to do a dry run or where to write the results; that's in cmd/bpfix.go 47type FixRequest struct { 48 steps []fixStep 49} 50 51type fixStep struct { 52 name string 53 fix func(f *Fixer) error 54} 55 56var fixSteps = []fixStep{ 57 { 58 name: "simplifyKnownRedundantVariables", 59 fix: runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther), 60 }, 61 { 62 name: "rewriteIncorrectAndroidmkPrebuilts", 63 fix: rewriteIncorrectAndroidmkPrebuilts, 64 }, 65 { 66 name: "rewriteCtsModuleTypes", 67 fix: rewriteCtsModuleTypes, 68 }, 69 { 70 name: "rewriteIncorrectAndroidmkAndroidLibraries", 71 fix: rewriteIncorrectAndroidmkAndroidLibraries, 72 }, 73 { 74 name: "rewriteTestModuleTypes", 75 fix: rewriteTestModuleTypes, 76 }, 77 { 78 name: "rewriteAndroidmkJavaLibs", 79 fix: rewriteAndroidmkJavaLibs, 80 }, 81 { 82 name: "rewriteJavaStaticLibs", 83 fix: rewriteJavaStaticLibs, 84 }, 85 { 86 name: "rewritePrebuiltEtc", 87 fix: rewriteAndroidmkPrebuiltEtc, 88 }, 89 { 90 name: "mergeMatchingModuleProperties", 91 fix: runPatchListMod(mergeMatchingModuleProperties), 92 }, 93 { 94 name: "reorderCommonProperties", 95 fix: runPatchListMod(reorderCommonProperties), 96 }, 97 { 98 name: "removeTags", 99 fix: runPatchListMod(removeTags), 100 }, 101 { 102 name: "rewriteAndroidTest", 103 fix: rewriteAndroidTest, 104 }, 105} 106 107func NewFixRequest() FixRequest { 108 return FixRequest{} 109} 110 111func (r FixRequest) AddAll() (result FixRequest) { 112 result.steps = append([]fixStep(nil), r.steps...) 113 result.steps = append(result.steps, fixSteps...) 114 return result 115} 116 117type Fixer struct { 118 tree *parser.File 119} 120 121func NewFixer(tree *parser.File) *Fixer { 122 fixer := &Fixer{tree} 123 124 // make a copy of the tree 125 fixer.reparse() 126 127 return fixer 128} 129 130// Fix repeatedly applies the fixes listed in the given FixRequest to the given File 131// until there is no fix that affects the tree 132func (f *Fixer) Fix(config FixRequest) (*parser.File, error) { 133 prevIdentifier, err := f.fingerprint() 134 if err != nil { 135 return nil, err 136 } 137 138 maxNumIterations := 20 139 i := 0 140 for { 141 err = f.fixTreeOnce(config) 142 newIdentifier, err := f.fingerprint() 143 if err != nil { 144 return nil, err 145 } 146 if bytes.Equal(newIdentifier, prevIdentifier) { 147 break 148 } 149 prevIdentifier = newIdentifier 150 // any errors from a previous iteration generally get thrown away and overwritten by errors on the next iteration 151 152 // detect infinite loop 153 i++ 154 if i >= maxNumIterations { 155 return nil, fmt.Errorf("Applied fixes %d times and yet the tree continued to change. Is there an infinite loop?", i) 156 } 157 } 158 return f.tree, err 159} 160 161// returns a unique identifier for the given tree that can be used to determine whether the tree changed 162func (f *Fixer) fingerprint() (fingerprint []byte, err error) { 163 bytes, err := parser.Print(f.tree) 164 if err != nil { 165 return nil, err 166 } 167 return bytes, nil 168} 169 170func (f *Fixer) reparse() ([]byte, error) { 171 buf, err := parser.Print(f.tree) 172 if err != nil { 173 return nil, err 174 } 175 newTree, err := parse(f.tree.Name, bytes.NewReader(buf)) 176 if err != nil { 177 return nil, err 178 } 179 f.tree = newTree 180 return buf, nil 181} 182 183func parse(name string, r io.Reader) (*parser.File, error) { 184 tree, errs := parser.Parse(name, r, parser.NewScope(nil)) 185 if errs != nil { 186 s := "parse error: " 187 for _, err := range errs { 188 s += "\n" + err.Error() 189 } 190 return nil, errors.New(s) 191 } 192 return tree, nil 193} 194 195func (f *Fixer) fixTreeOnce(config FixRequest) error { 196 for _, fix := range config.steps { 197 err := fix.fix(f) 198 if err != nil { 199 return err 200 } 201 } 202 return nil 203} 204 205func simplifyKnownPropertiesDuplicatingEachOther(mod *parser.Module, buf []byte, patchList *parser.PatchList) error { 206 // remove from local_include_dirs anything in export_include_dirs 207 return removeMatchingModuleListProperties(mod, patchList, 208 "export_include_dirs", "local_include_dirs") 209} 210 211func rewriteIncorrectAndroidmkPrebuilts(f *Fixer) error { 212 for _, def := range f.tree.Defs { 213 mod, ok := def.(*parser.Module) 214 if !ok { 215 continue 216 } 217 if mod.Type != "java_import" { 218 continue 219 } 220 host, _ := getLiteralBoolPropertyValue(mod, "host") 221 if host { 222 mod.Type = "java_import_host" 223 removeProperty(mod, "host") 224 } 225 srcs, ok := getLiteralListProperty(mod, "srcs") 226 if !ok { 227 continue 228 } 229 if len(srcs.Values) == 0 { 230 continue 231 } 232 src, ok := srcs.Values[0].(*parser.String) 233 if !ok { 234 continue 235 } 236 switch filepath.Ext(src.Value) { 237 case ".jar": 238 renameProperty(mod, "srcs", "jars") 239 240 case ".aar": 241 renameProperty(mod, "srcs", "aars") 242 mod.Type = "android_library_import" 243 244 // An android_library_import doesn't get installed, so setting "installable = false" isn't supported 245 removeProperty(mod, "installable") 246 } 247 } 248 249 return nil 250} 251 252func rewriteCtsModuleTypes(f *Fixer) error { 253 for _, def := range f.tree.Defs { 254 mod, ok := def.(*parser.Module) 255 if !ok { 256 continue 257 } 258 259 if mod.Type != "cts_support_package" && mod.Type != "cts_package" && 260 mod.Type != "cts_target_java_library" && 261 mod.Type != "cts_host_java_library" { 262 263 continue 264 } 265 266 var defStr string 267 switch mod.Type { 268 case "cts_support_package": 269 mod.Type = "android_test" 270 defStr = "cts_support_defaults" 271 case "cts_package": 272 mod.Type = "android_test" 273 defStr = "cts_defaults" 274 case "cts_target_java_library": 275 mod.Type = "java_library" 276 defStr = "cts_defaults" 277 case "cts_host_java_library": 278 mod.Type = "java_library_host" 279 defStr = "cts_defaults" 280 } 281 282 defaults := &parser.Property{ 283 Name: "defaults", 284 Value: &parser.List{ 285 Values: []parser.Expression{ 286 &parser.String{ 287 Value: defStr, 288 }, 289 }, 290 }, 291 } 292 mod.Properties = append(mod.Properties, defaults) 293 } 294 295 return nil 296} 297 298func rewriteIncorrectAndroidmkAndroidLibraries(f *Fixer) error { 299 for _, def := range f.tree.Defs { 300 mod, ok := def.(*parser.Module) 301 if !ok { 302 continue 303 } 304 305 if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") { 306 continue 307 } 308 309 hasAndroidLibraries := hasNonEmptyLiteralListProperty(mod, "android_libs") 310 hasStaticAndroidLibraries := hasNonEmptyLiteralListProperty(mod, "android_static_libs") 311 hasResourceDirs := hasNonEmptyLiteralListProperty(mod, "resource_dirs") 312 313 if hasAndroidLibraries || hasStaticAndroidLibraries || hasResourceDirs { 314 if mod.Type == "java_library_static" || mod.Type == "java_library" { 315 mod.Type = "android_library" 316 } 317 } 318 319 if mod.Type == "java_import" && !hasStaticAndroidLibraries { 320 removeProperty(mod, "android_static_libs") 321 } 322 323 // These may conflict with existing libs and static_libs properties, but the 324 // mergeMatchingModuleProperties pass will fix it. 325 renameProperty(mod, "shared_libs", "libs") 326 renameProperty(mod, "android_libs", "libs") 327 renameProperty(mod, "android_static_libs", "static_libs") 328 } 329 330 return nil 331} 332 333// rewriteTestModuleTypes looks for modules that are identifiable as tests but for which Make doesn't have a separate 334// module class, and moves them to the appropriate Soong module type. 335func rewriteTestModuleTypes(f *Fixer) error { 336 for _, def := range f.tree.Defs { 337 mod, ok := def.(*parser.Module) 338 if !ok { 339 continue 340 } 341 342 if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") { 343 continue 344 } 345 346 hasInstrumentationFor := hasNonEmptyLiteralStringProperty(mod, "instrumentation_for") 347 tags, _ := getLiteralListPropertyValue(mod, "tags") 348 349 var hasTestsTag bool 350 for _, tag := range tags { 351 if tag == "tests" { 352 hasTestsTag = true 353 } 354 } 355 356 isTest := hasInstrumentationFor || hasTestsTag 357 358 if isTest { 359 switch mod.Type { 360 case "android_app": 361 mod.Type = "android_test" 362 case "java_library", "java_library_installable": 363 mod.Type = "java_test" 364 case "java_library_host": 365 mod.Type = "java_test_host" 366 } 367 } 368 } 369 370 return nil 371} 372 373// rewriteJavaStaticLibs rewrites java_library_static into java_library 374func rewriteJavaStaticLibs(f *Fixer) error { 375 for _, def := range f.tree.Defs { 376 mod, ok := def.(*parser.Module) 377 if !ok { 378 continue 379 } 380 381 if mod.Type == "java_library_static" { 382 mod.Type = "java_library" 383 } 384 } 385 386 return nil 387} 388 389// rewriteAndroidmkJavaLibs rewrites java_library_installable into java_library plus installable: true 390func rewriteAndroidmkJavaLibs(f *Fixer) error { 391 for _, def := range f.tree.Defs { 392 mod, ok := def.(*parser.Module) 393 if !ok { 394 continue 395 } 396 397 if mod.Type != "java_library_installable" { 398 continue 399 } 400 401 mod.Type = "java_library" 402 403 _, hasInstallable := mod.GetProperty("installable") 404 if !hasInstallable { 405 prop := &parser.Property{ 406 Name: "installable", 407 Value: &parser.Bool{ 408 Value: true, 409 }, 410 } 411 mod.Properties = append(mod.Properties, prop) 412 } 413 } 414 415 return nil 416} 417 418// Helper function to get the value of a string-valued property in a given compound property. 419func getStringProperty(prop *parser.Property, fieldName string) string { 420 if propsAsMap, ok := prop.Value.(*parser.Map); ok { 421 for _, propField := range propsAsMap.Properties { 422 if fieldName == propField.Name { 423 if propFieldAsString, ok := propField.Value.(*parser.String); ok { 424 return propFieldAsString.Value 425 } else { 426 return "" 427 } 428 } 429 } 430 } 431 return "" 432} 433 434// Create sub_dir: attribute for the given path 435func makePrebuiltEtcDestination(mod *parser.Module, path string) { 436 mod.Properties = append(mod.Properties, &parser.Property{ 437 Name: "sub_dir", 438 Value: &parser.String{Value: path}, 439 }) 440} 441 442// Set the value of the given attribute to the error message 443func indicateAttributeError(mod *parser.Module, attributeName string, format string, a ...interface{}) error { 444 msg := fmt.Sprintf(format, a...) 445 mod.Properties = append(mod.Properties, &parser.Property{ 446 Name: attributeName, 447 Value: &parser.String{Value: "ERROR: " + msg}, 448 }) 449 return errors.New(msg) 450} 451 452// If a variable is LOCAL_MODULE, get its value from the 'name' attribute. 453// This handles the statement 454// LOCAL_SRC_FILES := $(LOCAL_MODULE) 455// which occurs often. 456func resolveLocalModule(mod *parser.Module, val parser.Expression) parser.Expression { 457 if varLocalName, ok := val.(*parser.Variable); ok { 458 if varLocalName.Name == "LOCAL_MODULE" { 459 if v, ok := getLiteralStringProperty(mod, "name"); ok { 460 return v 461 } 462 } 463 } 464 return val 465} 466 467// A prefix to strip before setting 'filename' attribute and an array of boolean attributes to set. 468type filenamePrefixToFlags struct { 469 prefix string 470 flags []string 471} 472 473var localModulePathRewrite = map[string][]filenamePrefixToFlags{ 474 "HOST_OUT": {{prefix: "/etc"}}, 475 "PRODUCT_OUT": {{prefix: "/system/etc"}, {prefix: "/vendor/etc", flags: []string{"proprietary"}}}, 476 "TARGET_OUT": {{prefix: "/etc"}}, 477 "TARGET_OUT_ETC": {{prefix: ""}}, 478 "TARGET_OUT_PRODUCT": {{prefix: "/etc", flags: []string{"product_specific"}}}, 479 "TARGET_OUT_PRODUCT_ETC": {{prefix: "", flags: []string{"product_specific"}}}, 480 "TARGET_OUT_ODM": {{prefix: "/etc", flags: []string{"device_specific"}}}, 481 "TARGET_OUT_PRODUCT_SERVICES": {{prefix: "/etc", flags: []string{"product_services_specific"}}}, 482 "TARGET_OUT_PRODUCT_SERVICES_ETC": {{prefix: "", flags: []string{"product_services_specific"}}}, 483 "TARGET_OUT_VENDOR": {{prefix: "/etc", flags: []string{"proprietary"}}}, 484 "TARGET_OUT_VENDOR_ETC": {{prefix: "", flags: []string{"proprietary"}}}, 485 "TARGET_RECOVERY_ROOT_OUT": {{prefix: "/system/etc", flags: []string{"recovery"}}}, 486} 487 488// rewriteAndroidPrebuiltEtc fixes prebuilt_etc rule 489func rewriteAndroidmkPrebuiltEtc(f *Fixer) error { 490 for _, def := range f.tree.Defs { 491 mod, ok := def.(*parser.Module) 492 if !ok { 493 continue 494 } 495 496 if mod.Type != "prebuilt_etc" && mod.Type != "prebuilt_etc_host" { 497 continue 498 } 499 500 // The rewriter converts LOCAL_SRC_FILES to `srcs` attribute. Convert 501 // it to 'src' attribute (which is where the file is installed). If the 502 // value 'srcs' is a list, we can convert it only if it contains a single 503 // element. 504 if srcs, ok := mod.GetProperty("srcs"); ok { 505 if srcList, ok := srcs.Value.(*parser.List); ok { 506 removeProperty(mod, "srcs") 507 if len(srcList.Values) == 1 { 508 mod.Properties = append(mod.Properties, 509 &parser.Property{Name: "src", NamePos: srcs.NamePos, ColonPos: srcs.ColonPos, Value: resolveLocalModule(mod, srcList.Values[0])}) 510 } else if len(srcList.Values) > 1 { 511 indicateAttributeError(mod, "src", "LOCAL_SRC_FILES should contain at most one item") 512 } 513 } else if _, ok = srcs.Value.(*parser.Variable); ok { 514 removeProperty(mod, "srcs") 515 mod.Properties = append(mod.Properties, 516 &parser.Property{Name: "src", NamePos: srcs.NamePos, ColonPos: srcs.ColonPos, Value: resolveLocalModule(mod, srcs.Value)}) 517 } else { 518 renameProperty(mod, "srcs", "src") 519 } 520 } 521 522 // The rewriter converts LOCAL_MODULE_PATH attribute into a struct attribute 523 // 'local_module_path'. Analyze its contents and create the correct sub_dir:, 524 // filename: and boolean attributes combination 525 const local_module_path = "local_module_path" 526 if prop_local_module_path, ok := mod.GetProperty(local_module_path); ok { 527 removeProperty(mod, local_module_path) 528 prefixVariableName := getStringProperty(prop_local_module_path, "var") 529 path := getStringProperty(prop_local_module_path, "fixed") 530 if prefixRewrites, ok := localModulePathRewrite[prefixVariableName]; ok { 531 rewritten := false 532 for _, prefixRewrite := range prefixRewrites { 533 if path == prefixRewrite.prefix { 534 rewritten = true 535 } else if trimmedPath := strings.TrimPrefix(path, prefixRewrite.prefix+"/"); trimmedPath != path { 536 makePrebuiltEtcDestination(mod, trimmedPath) 537 rewritten = true 538 } 539 if rewritten { 540 for _, flag := range prefixRewrite.flags { 541 mod.Properties = append(mod.Properties, &parser.Property{Name: flag, Value: &parser.Bool{Value: true, Token: "true"}}) 542 } 543 break 544 } 545 } 546 if !rewritten { 547 expectedPrefices := "" 548 sep := "" 549 for _, prefixRewrite := range prefixRewrites { 550 expectedPrefices += sep 551 sep = ", " 552 expectedPrefices += prefixRewrite.prefix 553 } 554 return indicateAttributeError(mod, "filename", 555 "LOCAL_MODULE_PATH value under $(%s) should start with %s", prefixVariableName, expectedPrefices) 556 } 557 if prefixVariableName == "HOST_OUT" { 558 mod.Type = "prebuilt_etc_host" 559 } 560 } else { 561 return indicateAttributeError(mod, "filename", "Cannot handle $(%s) for the prebuilt_etc", prefixVariableName) 562 } 563 } 564 } 565 return nil 566} 567 568func rewriteAndroidTest(f *Fixer) error { 569 for _, def := range f.tree.Defs { 570 mod, ok := def.(*parser.Module) 571 if !(ok && mod.Type == "android_test") { 572 continue 573 } 574 // The rewriter converts LOCAL_MODULE_PATH attribute into a struct attribute 575 // 'local_module_path'. For the android_test module, it should be $(TARGET_OUT_DATA_APPS), 576 // that is, `local_module_path: { var: "TARGET_OUT_DATA_APPS"}` 577 const local_module_path = "local_module_path" 578 if prop_local_module_path, ok := mod.GetProperty(local_module_path); ok { 579 removeProperty(mod, local_module_path) 580 prefixVariableName := getStringProperty(prop_local_module_path, "var") 581 path := getStringProperty(prop_local_module_path, "fixed") 582 if prefixVariableName == "TARGET_OUT_DATA_APPS" && path == "" { 583 continue 584 } 585 return indicateAttributeError(mod, "filename", 586 "Only LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) is allowed for the android_test") 587 } 588 } 589 return nil 590} 591 592func runPatchListMod(modFunc func(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error) func(*Fixer) error { 593 return func(f *Fixer) error { 594 // Make sure all the offsets are accurate 595 buf, err := f.reparse() 596 if err != nil { 597 return err 598 } 599 600 var patchlist parser.PatchList 601 for _, def := range f.tree.Defs { 602 mod, ok := def.(*parser.Module) 603 if !ok { 604 continue 605 } 606 607 err := modFunc(mod, buf, &patchlist) 608 if err != nil { 609 return err 610 } 611 } 612 613 newBuf := new(bytes.Buffer) 614 err = patchlist.Apply(bytes.NewReader(buf), newBuf) 615 if err != nil { 616 return err 617 } 618 619 // Save a copy of the buffer to print for errors below 620 bufCopy := append([]byte(nil), newBuf.Bytes()...) 621 622 newTree, err := parse(f.tree.Name, newBuf) 623 if err != nil { 624 return fmt.Errorf("Failed to parse: %v\nBuffer:\n%s", err, string(bufCopy)) 625 } 626 627 f.tree = newTree 628 629 return nil 630 } 631} 632 633var commonPropertyPriorities = []string{ 634 "name", 635 "defaults", 636 "device_supported", 637 "host_supported", 638 "installable", 639} 640 641func reorderCommonProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error { 642 if len(mod.Properties) == 0 { 643 return nil 644 } 645 646 pos := mod.LBracePos.Offset + 1 647 stage := "" 648 649 for _, name := range commonPropertyPriorities { 650 idx := propertyIndex(mod.Properties, name) 651 if idx == -1 { 652 continue 653 } 654 if idx == 0 { 655 err := patchlist.Add(pos, pos, stage) 656 if err != nil { 657 return err 658 } 659 stage = "" 660 661 pos = mod.Properties[0].End().Offset + 1 662 mod.Properties = mod.Properties[1:] 663 continue 664 } 665 666 prop := mod.Properties[idx] 667 mod.Properties = append(mod.Properties[:idx], mod.Properties[idx+1:]...) 668 669 stage += string(buf[prop.Pos().Offset : prop.End().Offset+1]) 670 671 err := patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, "") 672 if err != nil { 673 return err 674 } 675 } 676 677 if stage != "" { 678 err := patchlist.Add(pos, pos, stage) 679 if err != nil { 680 return err 681 } 682 } 683 684 return nil 685} 686 687func removeTags(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error { 688 prop, ok := mod.GetProperty("tags") 689 if !ok { 690 return nil 691 } 692 list, ok := prop.Value.(*parser.List) 693 if !ok { 694 return nil 695 } 696 697 replaceStr := "" 698 699 for _, item := range list.Values { 700 str, ok := item.(*parser.String) 701 if !ok { 702 replaceStr += fmt.Sprintf("// ERROR: Unable to parse tag %q\n", item) 703 continue 704 } 705 706 switch str.Value { 707 case "optional": 708 continue 709 case "debug": 710 replaceStr += `// WARNING: Module tags are not supported in Soong. 711 // Add this module to PRODUCT_PACKAGES_DEBUG in your product file if you want to 712 // force installation for -userdebug and -eng builds. 713 ` 714 case "eng": 715 replaceStr += `// WARNING: Module tags are not supported in Soong. 716 // Add this module to PRODUCT_PACKAGES_ENG in your product file if you want to 717 // force installation for -eng builds. 718 ` 719 case "tests": 720 switch { 721 case strings.Contains(mod.Type, "cc_test"), 722 strings.Contains(mod.Type, "cc_library_static"), 723 strings.Contains(mod.Type, "java_test"), 724 mod.Type == "android_test": 725 continue 726 case strings.Contains(mod.Type, "cc_lib"): 727 replaceStr += `// WARNING: Module tags are not supported in Soong. 728 // To make a shared library only for tests, use the "cc_test_library" module 729 // type. If you don't use gtest, set "gtest: false". 730 ` 731 case strings.Contains(mod.Type, "cc_bin"): 732 replaceStr += `// WARNING: Module tags are not supported in Soong. 733 // For native test binaries, use the "cc_test" module type. Some differences: 734 // - If you don't use gtest, set "gtest: false" 735 // - Binaries will be installed into /data/nativetest[64]/<name>/<name> 736 // - Both 32 & 64 bit versions will be built (as appropriate) 737 ` 738 case strings.Contains(mod.Type, "java_lib"): 739 replaceStr += `// WARNING: Module tags are not supported in Soong. 740 // For JUnit or similar tests, use the "java_test" module type. A dependency on 741 // Junit will be added by default, if it is using some other runner, set "junit: false". 742 ` 743 case mod.Type == "android_app": 744 replaceStr += `// WARNING: Module tags are not supported in Soong. 745 // For JUnit or instrumentataion app tests, use the "android_test" module type. 746 ` 747 default: 748 replaceStr += `// WARNING: Module tags are not supported in Soong. 749 // In most cases, tests are now identified by their module type: 750 // cc_test, java_test, python_test 751 ` 752 } 753 default: 754 replaceStr += fmt.Sprintf("// WARNING: Unknown module tag %q\n", str.Value) 755 } 756 } 757 758 return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, replaceStr) 759} 760 761func mergeMatchingModuleProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error { 762 return mergeMatchingProperties(&mod.Properties, buf, patchlist) 763} 764 765func mergeMatchingProperties(properties *[]*parser.Property, buf []byte, patchlist *parser.PatchList) error { 766 seen := make(map[string]*parser.Property) 767 for i := 0; i < len(*properties); i++ { 768 property := (*properties)[i] 769 if prev, exists := seen[property.Name]; exists { 770 err := mergeProperties(prev, property, buf, patchlist) 771 if err != nil { 772 return err 773 } 774 *properties = append((*properties)[:i], (*properties)[i+1:]...) 775 } else { 776 seen[property.Name] = property 777 if mapProperty, ok := property.Value.(*parser.Map); ok { 778 err := mergeMatchingProperties(&mapProperty.Properties, buf, patchlist) 779 if err != nil { 780 return err 781 } 782 } 783 } 784 } 785 return nil 786} 787 788func mergeProperties(a, b *parser.Property, buf []byte, patchlist *parser.PatchList) error { 789 // The value of one of the properties may be a variable reference with no type assigned 790 // Bail out in this case. Soong will notice duplicate entries and will tell to merge them. 791 if _, isVar := a.Value.(*parser.Variable); isVar { 792 return nil 793 } 794 if _, isVar := b.Value.(*parser.Variable); isVar { 795 return nil 796 } 797 if a.Value.Type() != b.Value.Type() { 798 return fmt.Errorf("type mismatch when merging properties %q: %s and %s", a.Name, a.Value.Type(), b.Value.Type()) 799 } 800 801 switch a.Value.Type() { 802 case parser.StringType: 803 return fmt.Errorf("conflicting definitions of string property %q", a.Name) 804 case parser.ListType: 805 return mergeListProperties(a, b, buf, patchlist) 806 } 807 808 return nil 809} 810 811func mergeListProperties(a, b *parser.Property, buf []byte, patchlist *parser.PatchList) error { 812 aval, oka := a.Value.(*parser.List) 813 bval, okb := b.Value.(*parser.List) 814 if !oka || !okb { 815 // Merging expressions not supported yet 816 return nil 817 } 818 819 s := string(buf[bval.LBracePos.Offset+1 : bval.RBracePos.Offset]) 820 if bval.LBracePos.Line != bval.RBracePos.Line { 821 if s[0] != '\n' { 822 panic("expected \n") 823 } 824 // If B is a multi line list, skip the first "\n" in case A already has a trailing "\n" 825 s = s[1:] 826 } 827 if aval.LBracePos.Line == aval.RBracePos.Line { 828 // A is a single line list with no trailing comma 829 if len(aval.Values) > 0 { 830 s = "," + s 831 } 832 } 833 834 err := patchlist.Add(aval.RBracePos.Offset, aval.RBracePos.Offset, s) 835 if err != nil { 836 return err 837 } 838 err = patchlist.Add(b.NamePos.Offset, b.End().Offset+2, "") 839 if err != nil { 840 return err 841 } 842 843 return nil 844} 845 846// removes from <items> every item present in <removals> 847func filterExpressionList(patchList *parser.PatchList, items *parser.List, removals *parser.List) { 848 writeIndex := 0 849 for _, item := range items.Values { 850 included := true 851 for _, removal := range removals.Values { 852 equal, err := parser.ExpressionsAreSame(item, removal) 853 if err != nil { 854 continue 855 } 856 if equal { 857 included = false 858 break 859 } 860 } 861 if included { 862 items.Values[writeIndex] = item 863 writeIndex++ 864 } else { 865 patchList.Add(item.Pos().Offset, item.End().Offset+2, "") 866 } 867 } 868 items.Values = items.Values[:writeIndex] 869} 870 871// Remove each modules[i].Properties[<legacyName>][j] that matches a modules[i].Properties[<canonicalName>][k] 872func removeMatchingModuleListProperties(mod *parser.Module, patchList *parser.PatchList, canonicalName string, legacyName string) error { 873 legacyProp, ok := mod.GetProperty(legacyName) 874 if !ok { 875 return nil 876 } 877 legacyList, ok := legacyProp.Value.(*parser.List) 878 if !ok || len(legacyList.Values) == 0 { 879 return nil 880 } 881 canonicalList, ok := getLiteralListProperty(mod, canonicalName) 882 if !ok { 883 return nil 884 } 885 886 localPatches := parser.PatchList{} 887 filterExpressionList(&localPatches, legacyList, canonicalList) 888 889 if len(legacyList.Values) == 0 { 890 patchList.Add(legacyProp.Pos().Offset, legacyProp.End().Offset+2, "") 891 } else { 892 for _, p := range localPatches { 893 patchList.Add(p.Start, p.End, p.Replacement) 894 } 895 } 896 897 return nil 898} 899 900func hasNonEmptyLiteralListProperty(mod *parser.Module, name string) bool { 901 list, found := getLiteralListProperty(mod, name) 902 return found && len(list.Values) > 0 903} 904 905func hasNonEmptyLiteralStringProperty(mod *parser.Module, name string) bool { 906 s, found := getLiteralStringPropertyValue(mod, name) 907 return found && len(s) > 0 908} 909 910func getLiteralListProperty(mod *parser.Module, name string) (list *parser.List, found bool) { 911 prop, ok := mod.GetProperty(name) 912 if !ok { 913 return nil, false 914 } 915 list, ok = prop.Value.(*parser.List) 916 return list, ok 917} 918 919func getLiteralListPropertyValue(mod *parser.Module, name string) (list []string, found bool) { 920 listValue, ok := getLiteralListProperty(mod, name) 921 if !ok { 922 return nil, false 923 } 924 for _, v := range listValue.Values { 925 stringValue, ok := v.(*parser.String) 926 if !ok { 927 return nil, false 928 } 929 list = append(list, stringValue.Value) 930 } 931 932 return list, true 933} 934 935func getLiteralStringProperty(mod *parser.Module, name string) (s *parser.String, found bool) { 936 prop, ok := mod.GetProperty(name) 937 if !ok { 938 return nil, false 939 } 940 s, ok = prop.Value.(*parser.String) 941 return s, ok 942} 943 944func getLiteralStringPropertyValue(mod *parser.Module, name string) (s string, found bool) { 945 stringValue, ok := getLiteralStringProperty(mod, name) 946 if !ok { 947 return "", false 948 } 949 950 return stringValue.Value, true 951} 952 953func getLiteralBoolProperty(mod *parser.Module, name string) (b *parser.Bool, found bool) { 954 prop, ok := mod.GetProperty(name) 955 if !ok { 956 return nil, false 957 } 958 b, ok = prop.Value.(*parser.Bool) 959 return b, ok 960} 961 962func getLiteralBoolPropertyValue(mod *parser.Module, name string) (s bool, found bool) { 963 boolValue, ok := getLiteralBoolProperty(mod, name) 964 if !ok { 965 return false, false 966 } 967 968 return boolValue.Value, true 969} 970 971func propertyIndex(props []*parser.Property, propertyName string) int { 972 for i, prop := range props { 973 if prop.Name == propertyName { 974 return i 975 } 976 } 977 return -1 978} 979 980func renameProperty(mod *parser.Module, from, to string) { 981 for _, prop := range mod.Properties { 982 if prop.Name == from { 983 prop.Name = to 984 } 985 } 986} 987 988func removeProperty(mod *parser.Module, propertyName string) { 989 newList := make([]*parser.Property, 0, len(mod.Properties)) 990 for _, prop := range mod.Properties { 991 if prop.Name != propertyName { 992 newList = append(newList, prop) 993 } 994 } 995 mod.Properties = newList 996} 997