• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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