Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  bpfix.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// This file implements the logic of bpfix and also provides a programmatic interface

package bpfix

import (
 "bytes"
 "errors"
 "flag"
 "fmt"
 "io"
 "io/ioutil"
 "os"
 "path/filepath"
 "reflect"
 "sort"
 "strings"

 "github.com/google/blueprint/parser"
 "github.com/google/blueprint/pathtools"
)

// Reformat takes a blueprint file as a string and returns a formatted version
func Reformat(input string) (string, error) {
 tree, err := parse("<string>", bytes.NewBufferString(input))
 if err != nil {
  return "", err
 }

 res, err := parser.Print(tree)
 if err != nil {
  return "", err
 }

 return string(res), nil
}

// A FixRequest specifies the details of which fixes to apply to an individual file
// A FixRequest doesn't specify whether to do a dry run or where to write the results; that's in cmd/bpfix.go
type FixRequest struct {
 steps []FixStep
}
type FixStepsExtension struct {
 Name  string
 Steps []FixStep
}

type FixStep struct {
 Name string
 Fix  func(f *Fixer) error
}

var fixStepsExtensions = []*FixStepsExtension(nil)

func RegisterFixStepExtension(extension *FixStepsExtension) {
 fixStepsExtensions = append(fixStepsExtensions, extension)
}

var fixSteps = []FixStep{
 {
  Name: "simplifyKnownRedundantVariables",
  Fix:  runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther),
 },
 {
  Name: "rewriteIncorrectAndroidmkPrebuilts",
  Fix:  rewriteIncorrectAndroidmkPrebuilts,
 },
 {
  Name: "rewriteCtsModuleTypes",
  Fix:  rewriteCtsModuleTypes,
 },
 {
  Name: "rewriteIncorrectAndroidmkAndroidLibraries",
  Fix:  rewriteIncorrectAndroidmkAndroidLibraries,
 },
 {
  Name: "rewriteTestModuleTypes",
  Fix:  rewriteTestModuleTypes,
 },
 {
  Name: "rewriteAndroidmkJavaLibs",
  Fix:  rewriteAndroidmkJavaLibs,
 },
 {
  Name: "rewriteJavaStaticLibs",
  Fix:  rewriteJavaStaticLibs,
 },
 {
  Name: "rewritePrebuiltEtc",
  Fix:  rewriteAndroidmkPrebuiltEtc,
 },
 {
  Name: "mergeMatchingModuleProperties",
  Fix:  runPatchListMod(mergeMatchingModuleProperties),
 },
 {
  Name: "reorderCommonProperties",
  Fix:  runPatchListMod(reorderCommonProperties),
 },
 {
  Name: "removeTags",
  Fix:  runPatchListMod(removeTags),
 },
 {
  Name: "rewriteAndroidTest",
  Fix:  rewriteAndroidTest,
 },
 {
  Name: "rewriteAndroidAppImport",
  Fix:  rewriteAndroidAppImport,
 },
 {
  Name: "removeEmptyLibDependencies",
  Fix:  removeEmptyLibDependencies,
 },
 {
  Name: "removeHidlInterfaceTypes",
  Fix:  removeHidlInterfaceTypes,
 },
 {
  Name: "removeSoongConfigBoolVariable",
  Fix:  removeSoongConfigBoolVariable,
 },
 {
  Name: "removePdkProperty",
  Fix:  runPatchListMod(removeObsoleteProperty("product_variables.pdk")),
 },
 {
  Name: "removeScudoProperty",
  Fix:  runPatchListMod(removeObsoleteProperty("sanitize.scudo")),
 },
 {
  Name: "removeAndroidLicenseKinds",
  Fix:  runPatchListMod(removeIncorrectProperties("android_license_kinds")),
 },
 {
  Name: "removeAndroidLicenseConditions",
  Fix:  runPatchListMod(removeIncorrectProperties("android_license_conditions")),
 },
 {
  Name: "removeAndroidLicenseFiles",
  Fix:  runPatchListMod(removeIncorrectProperties("android_license_files")),
 },
 {
  Name: "formatFlagProperties",
  Fix:  runPatchListMod(formatFlagProperties),
 },
 {
  Name: "removeResourcesAndAssetsIfDefault",
  Fix:  removeResourceAndAssetsIfDefault,
 },
}

// for fix that only need to run once
var fixStepsOnce = []FixStep{
 {
  Name: "haveSameLicense",
  Fix:  haveSameLicense,
 },
 {
  Name: "rewriteLicenseProperties",
  Fix:  runPatchListMod(rewriteLicenseProperty(nil, "")),
 },
}

func NewFixRequest() FixRequest {
 return FixRequest{}
}

func (r FixRequest) AddAll() (result FixRequest) {
 result.steps = append([]FixStep(nil), r.steps...)
 result.steps = append(result.steps, fixSteps...)
 for _, extension := range fixStepsExtensions {
  result.steps = append(result.steps, extension.Steps...)
 }
 return result
}

func (r FixRequest) AddBase() (result FixRequest) {
 result.steps = append([]FixStep(nil), r.steps...)
 result.steps = append(result.steps, fixSteps...)
 return result
}

func (r FixRequest) AddMatchingExtensions(pattern string) (result FixRequest) {
 result.steps = append([]FixStep(nil), r.steps...)
 for _, extension := range fixStepsExtensions {
  if match, _ := filepath.Match(pattern, extension.Name); match {
   result.steps = append(result.steps, extension.Steps...)
  }
 }
 return result
}

type Fixer struct {
 tree *parser.File
}

func (f Fixer) Tree() *parser.File {
 return f.tree
}

func NewFixer(tree *parser.File) *Fixer {
 fixer := &Fixer{tree}

 // make a copy of the tree
 fixer.reparse()

 return fixer
}

// Fix repeatedly applies the fixes listed in the given FixRequest to the given File
// until there is no fix that affects the tree
func (f *Fixer) Fix(config FixRequest) (*parser.File, error) {
 prevIdentifier, err := f.fingerprint()
 if err != nil {
  return nil, err
 }

 // run fix that is expected to run once first
 configOnce := NewFixRequest()
 configOnce.steps = append(configOnce.steps, fixStepsOnce...)
 if len(configOnce.steps) > 0 {
  err = f.fixTreeOnce(configOnce)
  if err != nil {
   return nil, err
  }
 }

 maxNumIterations := 20
 i := 0
 for {
  err = f.fixTreeOnce(config)
  newIdentifier, err := f.fingerprint()
  if err != nil {
   return nil, err
  }
  if bytes.Equal(newIdentifier, prevIdentifier) {
   break
  }
  prevIdentifier = newIdentifier
  // any errors from a previous iteration generally get thrown away and overwritten by errors on the next iteration

  // detect infinite loop
  i++
  if i >= maxNumIterations {
   return nil, fmt.Errorf("Applied fixes %d times and yet the tree continued to change. Is there an infinite loop?", i)
  }
 }
 return f.tree, err
}

// returns a unique identifier for the given tree that can be used to determine whether the tree changed
func (f *Fixer) fingerprint() (fingerprint []byte, err error) {
 bytes, err := parser.Print(f.tree)
 if err != nil {
  return nil, err
 }
 return bytes, nil
}

func (f *Fixer) reparse() ([]byte, error) {
 buf, err := parser.Print(f.tree)
 if err != nil {
  return nil, err
 }
 newTree, err := parse(f.tree.Name, bytes.NewReader(buf))
 if err != nil {
  return nil, err
 }
 f.tree = newTree
 return buf, nil
}

func parse(name string, r io.Reader) (*parser.File, error) {
 tree, errs := parser.Parse(name, r)
 if errs != nil {
  s := "parse error: "
  for _, err := range errs {
   s += "\n" + err.Error()
  }
  return nil, errors.New(s)
 }
 return tree, nil
}

func (f *Fixer) fixTreeOnce(config FixRequest) error {
 for _, fix := range config.steps {
  err := fix.Fix(f)
  if err != nil {
   return err
  }
 }
 return nil
}

func simplifyKnownPropertiesDuplicatingEachOther(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
 // remove from local_include_dirs anything in export_include_dirs
 return removeMatchingModuleListProperties(mod, patchList,
  "export_include_dirs", "local_include_dirs")
}

func rewriteIncorrectAndroidmkPrebuilts(f *Fixer) error {
 for _, def := range f.tree.Defs {
  mod, ok := def.(*parser.Module)
  if !ok {
   continue
  }
  if mod.Type != "java_import" {
   continue
  }
  host, _ := getLiteralBoolPropertyValue(mod, "host")
  if host {
   mod.Type = "java_import_host"
   removeProperty(mod, "host")
  }
  srcs, ok := getLiteralListProperty(mod, "srcs")
  if !ok {
   continue
  }
  if len(srcs.Values) == 0 {
   continue
  }
  src, ok := srcs.Values[0].(*parser.String)
  if !ok {
   continue
  }
  switch filepath.Ext(src.Value) {
  case ".jar":
   renameProperty(mod, "srcs", "jars")

  case ".aar":
   renameProperty(mod, "srcs", "aars")
   mod.Type = "android_library_import"

   // An android_library_import doesn't get installed, so setting "installable = false" isn't supported
   removeProperty(mod, "installable")
  }
 }

 return nil
}

func rewriteCtsModuleTypes(f *Fixer) error {
 for _, def := range f.tree.Defs {
  mod, ok := def.(*parser.Module)
  if !ok {
   continue
  }

  if mod.Type != "cts_support_package" && mod.Type != "cts_package" &&
   mod.Type != "cts_target_java_library" &&
   mod.Type != "cts_host_java_library" {

   continue
  }

  var defStr string
  switch mod.Type {
  case "cts_support_package":
   mod.Type = "android_test_helper_app"
   defStr = "cts_support_defaults"
  case "cts_package":
   mod.Type = "android_test"
   defStr = "cts_defaults"
  case "cts_target_java_library":
   mod.Type = "java_library"
   defStr = "cts_defaults"
  case "cts_host_java_library":
   mod.Type = "java_library_host"
   defStr = "cts_defaults"
  }

  defaults := &parser.Property{
   Name: "defaults",
   Value: &parser.List{
    Values: []parser.Expression{
     &parser.String{
      Value: defStr,
     },
    },
   },
  }
  mod.Properties = append(mod.Properties, defaults)
 }

 return nil
}

func rewriteIncorrectAndroidmkAndroidLibraries(f *Fixer) error {
 for _, def := range f.tree.Defs {
  mod, ok := def.(*parser.Module)
  if !ok {
   continue
  }

  if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") {
   continue
  }

  hasAndroidLibraries := hasNonEmptyLiteralListProperty(mod, "android_libs")
  hasStaticAndroidLibraries := hasNonEmptyLiteralListProperty(mod, "android_static_libs")
  hasResourceDirs := hasNonEmptyLiteralListProperty(mod, "resource_dirs")

  if hasAndroidLibraries || hasStaticAndroidLibraries || hasResourceDirs {
   if mod.Type == "java_library_static" || mod.Type == "java_library" {
    mod.Type = "android_library"
   }
  }

  if mod.Type == "java_import" && !hasStaticAndroidLibraries {
   removeProperty(mod, "android_static_libs")
  }

  // These may conflict with existing libs and static_libs properties, but the
  // mergeMatchingModuleProperties pass will fix it.
  renameProperty(mod, "shared_libs", "libs")
  renameProperty(mod, "android_libs", "libs")
  renameProperty(mod, "android_static_libs", "static_libs")
 }

 return nil
}

// rewriteTestModuleTypes looks for modules that are identifiable as tests but for which Make doesn't have a separate
// module class, and moves them to the appropriate Soong module type.
func rewriteTestModuleTypes(f *Fixer) error {
 for _, def := range f.tree.Defs {
  mod, ok := def.(*parser.Module)
  if !ok {
   continue
  }

  if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") && mod.Type != "cc_binary" {
   continue
  }

  hasInstrumentationFor := hasNonEmptyLiteralStringProperty(mod, "instrumentation_for")
  hasTestSuites := hasNonEmptyLiteralListProperty(mod, "test_suites")
  tags, _ := getLiteralListPropertyValue(mod, "tags")

  var hasTestsTag bool
  for _, tag := range tags {
   if tag == "tests" {
    hasTestsTag = true
   }
  }

  isTest := hasInstrumentationFor || hasTestsTag || hasTestSuites

  if isTest {
   switch mod.Type {
   case "android_app":
    mod.Type = "android_test"
   case "android_app_import":
    mod.Type = "android_test_import"
   case "java_library", "java_library_installable":
    mod.Type = "java_test"
   case "java_library_host":
    mod.Type = "java_test_host"
   case "cc_binary":
    mod.Type = "cc_test"
   }
  }
 }

 return nil
}

// rewriteJavaStaticLibs rewrites java_library_static into java_library
func rewriteJavaStaticLibs(f *Fixer) error {
 for _, def := range f.tree.Defs {
  mod, ok := def.(*parser.Module)
  if !ok {
   continue
  }

  if mod.Type == "java_library_static" {
   mod.Type = "java_library"
  }
 }

 return nil
}

// rewriteAndroidmkJavaLibs rewrites java_library_installable into java_library plus installable: true
func rewriteAndroidmkJavaLibs(f *Fixer) error {
 for _, def := range f.tree.Defs {
  mod, ok := def.(*parser.Module)
  if !ok {
   continue
  }

  if mod.Type != "java_library_installable" {
   continue
  }

  mod.Type = "java_library"

  _, hasInstallable := mod.GetProperty("installable")
  if !hasInstallable {
   prop := &parser.Property{
    Name: "installable",
    Value: &parser.Bool{
     Value: true,
    },
   }
   mod.Properties = append(mod.Properties, prop)
  }
 }

 return nil
}

// Helper function to get the value of a string-valued property in a given compound property.
func getStringProperty(prop *parser.Property, fieldName string) string {
 if propsAsMap, ok := prop.Value.(*parser.Map); ok {
  for _, propField := range propsAsMap.Properties {
   if fieldName == propField.Name {
    if propFieldAsString, ok := propField.Value.(*parser.String); ok {
     return propFieldAsString.Value
    } else {
     return ""
    }
   }
  }
 }
 return ""
}

// Set the value of the given attribute to the error message
func indicateAttributeError(mod *parser.Module, attributeName string, format string, a ...interface{}) error {
 msg := fmt.Sprintf(format, a...)
 mod.Properties = append(mod.Properties, &parser.Property{
  Name:  attributeName,
  Value: &parser.String{Value: "ERROR: " + msg},
 })
 return errors.New(msg)
}

// If a variable is LOCAL_MODULE, get its value from the 'name' attribute.
// This handles the statement
//
// LOCAL_SRC_FILES := $(LOCAL_MODULE)
//
// which occurs often.
func resolveLocalModule(mod *parser.Module, val parser.Expression) parser.Expression {
 if varLocalName, ok := val.(*parser.Variable); ok {
  if varLocalName.Name == "LOCAL_MODULE" {
   if v, ok := getLiteralStringProperty(mod, "name"); ok {
    return v
   }
  }
 }
 return val
}

// etcPrebuiltModuleUpdate contains information on updating certain parts of a defined module such as:
//   - changing the module type from prebuilt_etc to a different one
//   - stripping the prefix of the install path based on the module type
//   - appending additional boolean properties to the prebuilt module
type etcPrebuiltModuleUpdate struct {
 // The prefix of the install path defined in local_module_path. The prefix is removed from local_module_path
 // before setting the 'filename' attribute.
 prefix string

 // There is only one prebuilt module type in makefiles. In Soong, there are multiple versions  of
 // prebuilts based on local_module_path. By default, it is "prebuilt_etc" if modType is blank. An
 // example is if the local_module_path contains $(TARGET_OUT)/usr/share, the module type is
 // considered as prebuilt_usr_share.
 modType string

 // Additional boolean attributes to be added in the prebuilt module. Each added boolean attribute
 // has a value of true.
 flags []string
}

func (f etcPrebuiltModuleUpdate) update(m *parser.Module, path string) bool {
 updated := false
 if path == f.prefix {
  updated = true
 } else if trimmedPath := strings.TrimPrefix(path, f.prefix+"/"); trimmedPath != path {
  m.Properties = append(m.Properties, &parser.Property{
   Name:  "relative_install_path",
   Value: &parser.String{Value: trimmedPath},
  })
  updated = true
 }
 if updated {
  for _, flag := range f.flags {
   m.Properties = append(m.Properties, &parser.Property{Name: flag, Value: &parser.Bool{Value: true, Token: "true"}})
  }
  if f.modType != "" {
   m.Type = f.modType
  }
 }
 return updated
}

var localModuleUpdate = map[string][]etcPrebuiltModuleUpdate{
 "HOST_OUT": {
  {prefix: "/etc", modType: "prebuilt_etc_host"},
  {prefix: "/usr/share", modType: "prebuilt_usr_share_host"},
  {prefix: "", modType: "prebuilt_root_host"},
 },
 "PRODUCT_OUT": {{prefix: "/system/etc"}, {prefix: "/vendor/etc", flags: []string{"proprietary"}}},
 "TARGET_OUT": {{prefix: "/usr/share", modType: "prebuilt_usr_share"}, {prefix: "/fonts", modType: "prebuilt_font"},
  {prefix: "/etc/firmware", modType: "prebuilt_firmware"}, {prefix: "/vendor/firmware", modType: "prebuilt_firmware", flags: []string{"proprietary"}},
  {prefix: "/etc"}},
 "TARGET_OUT_ETC":            {{prefix: "/firmware", modType: "prebuilt_firmware"}, {prefix: ""}},
 "TARGET_OUT_PRODUCT":        {{prefix: "/etc", flags: []string{"product_specific"}}, {prefix: "/fonts", modType: "prebuilt_font", flags: []string{"product_specific"}}},
 "TARGET_OUT_PRODUCT_ETC":    {{prefix: "", flags: []string{"product_specific"}}},
 "TARGET_OUT_ODM":            {{prefix: "/etc", flags: []string{"device_specific"}}},
 "TARGET_OUT_SYSTEM_EXT":     {{prefix: "/etc", flags: []string{"system_ext_specific"}}},
 "TARGET_OUT_SYSTEM_EXT_ETC": {{prefix: "", flags: []string{"system_ext_specific"}}},
 "TARGET_OUT_VENDOR":         {{prefix: "/etc", flags: []string{"proprietary"}}, {prefix: "/firmware", modType: "prebuilt_firmware", flags: []string{"proprietary"}}},
 "TARGET_OUT_VENDOR_ETC":     {{prefix: "", flags: []string{"proprietary"}}},
 "TARGET_RECOVERY_ROOT_OUT":  {{prefix: "/system/etc", flags: []string{"recovery"}}},
}

// rewriteAndroidPrebuiltEtc fixes prebuilt_etc rule
func rewriteAndroidmkPrebuiltEtc(f *Fixer) error {
 for _, def := range f.tree.Defs {
  mod, ok := def.(*parser.Module)
  if !ok {
   continue
  }

  if mod.Type != "prebuilt_etc" && mod.Type != "prebuilt_etc_host" {
   continue
  }

  // 'srcs' --> 'src' conversion
  convertToSingleSource(mod, "src")

  renameProperty(mod, "sub_dir", "relative_install_path")

  // The rewriter converts LOCAL_MODULE_PATH attribute into a struct attribute
  // 'local_module_path'. Analyze its contents and create the correct sub_dir:,
  // filename: and boolean attributes combination
  const local_module_path = "local_module_path"
  if prop_local_module_path, ok := mod.GetProperty(local_module_path); ok {
   removeProperty(mod, local_module_path)
   prefixVariableName := getStringProperty(prop_local_module_path, "var")
   if moduleUpdates, ok := localModuleUpdate[prefixVariableName]; ok {
    path := getStringProperty(prop_local_module_path, "fixed")
    updated := false
    for i := 0; i < len(moduleUpdates) && !updated; i++ {
     updated = moduleUpdates[i].update(mod, path)
    }
    if !updated {
     expectedPrefices := ""
     sep := ""
     for _, moduleUpdate := range moduleUpdates {
      expectedPrefices += sep
      sep = ", "
      expectedPrefices += moduleUpdate.prefix
     }
     return indicateAttributeError(mod, "filename",
      "LOCAL_MODULE_PATH value under $(%s) should start with %s", prefixVariableName, expectedPrefices)
    }
   } else {
    return indicateAttributeError(mod, "filename", "Cannot handle $(%s) for the prebuilt_etc", prefixVariableName)
   }
  }
 }
 return nil
}

func rewriteAndroidTest(f *Fixer) error {
 for _, def := range f.tree.Defs {
  mod, ok := def.(*parser.Module)
  if !ok {
   // The definition is not a module.
   continue
  }
  if mod.Type != "android_test" && mod.Type != "android_test_helper_app" {
   // The module is not an android_test or android_test_helper_app.
   continue
  }
  // The rewriter converts LOCAL_MODULE_PATH attribute into a struct attribute
  // 'local_module_path'. For the android_test module, it should be  $(TARGET_OUT_DATA_APPS),
  // that is, `local_module_path: { var: "TARGET_OUT_DATA_APPS"}`
  // 1. if the `key: val` pair matches, (key is `local_module_path`,
  //    and val is `{ var: "TARGET_OUT_DATA_APPS"}`), this property is removed;
  // 2. o/w, an error msg is thrown.
  const local_module_path = "local_module_path"
  if prop_local_module_path, ok := mod.GetProperty(local_module_path); ok {
   removeProperty(mod, local_module_path)
   prefixVariableName := getStringProperty(prop_local_module_path, "var")
   path := getStringProperty(prop_local_module_path, "fixed")
   if prefixVariableName == "TARGET_OUT_DATA_APPS" && path == "" {
    continue
   }
   return indicateAttributeError(mod, "filename",
    "Only LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) is allowed for the %s", mod.Type)
  }
 }
 return nil
}

func rewriteAndroidAppImport(f *Fixer) error {
 for _, def := range f.tree.Defs {
  mod, ok := def.(*parser.Module)
  if !(ok && mod.Type == "android_app_import") {
   continue
  }
  // 'srcs' --> 'apk' conversion
  convertToSingleSource(mod, "apk")
  // Handle special certificate value, "PRESIGNED".
  if cert, ok := mod.GetProperty("certificate"); ok {
   if certStr, ok := cert.Value.(*parser.String); ok {
    if certStr.Value == "PRESIGNED" {
     removeProperty(mod, "certificate")
     prop := &parser.Property{
      Name: "presigned",
      Value: &parser.Bool{
       Value: true,
      },
     }
     mod.Properties = append(mod.Properties, prop)
    }
   }
  }
 }
 return nil
}

func RewriteRuntimeResourceOverlay(f *Fixer) error {
 for _, def := range f.tree.Defs {
  mod, ok := def.(*parser.Module)
  if !(ok && mod.Type == "runtime_resource_overlay") {
   continue
  }
  // runtime_resource_overlays are always product specific in Make.
  if _, ok := mod.GetProperty("product_specific"); !ok {
   prop := &parser.Property{
    Name: "product_specific",
    Value: &parser.Bool{
     Value: true,
    },
   }
   mod.Properties = append(mod.Properties, prop)
  }
 }
 return nil
}

// Removes library dependencies which are empty (and restricted from usage in Soong)
func removeEmptyLibDependencies(f *Fixer) error {
 emptyLibraries := []string{
  "libhidltransport",
  "libhwbinder",
 }
 relevantFields := []string{
  "export_shared_lib_headers",
  "export_static_lib_headers",
  "static_libs",
  "whole_static_libs",
  "shared_libs",
 }
 for _, def := range f.tree.Defs {
  mod, ok := def.(*parser.Module)
  if !ok {
   continue
  }
  for _, field := range relevantFields {
   listValue, ok := getLiteralListProperty(mod, field)
   if !ok {
    continue
   }
   newValues := []parser.Expression{}
   for _, v := range listValue.Values {
    stringValue, ok := v.(*parser.String)
    if !ok {
     return fmt.Errorf("Expecting string for %s.%s fields", mod.Type, field)
    }
    if inList(stringValue.Value, emptyLibraries) {
     continue
    }
    newValues = append(newValues, stringValue)
   }
   if len(newValues) == 0 && len(listValue.Values) != 0 {
    removeProperty(mod, field)
   } else {
    listValue.Values = newValues
   }
  }
 }
 return nil
}

// Removes hidl_interface 'types' which are no longer needed
func removeHidlInterfaceTypes(f *Fixer) error {
 for _, def := range f.tree.Defs {
  mod, ok := def.(*parser.Module)
  if !(ok && mod.Type == "hidl_interface") {
   continue
  }
  removeProperty(mod, "types")
 }
 return nil
}

func removeSoongConfigBoolVariable(f *Fixer) error {
 found := map[string]bool{}
 newDefs := make([]parser.Definition, 0, len(f.tree.Defs))
 for _, def := range f.tree.Defs {
  if mod, ok := def.(*parser.Module); ok && mod.Type == "soong_config_bool_variable" {
   if name, ok := getLiteralStringPropertyValue(mod, "name"); ok {
    found[name] = true
   } else {
    return fmt.Errorf("Found soong_config_bool_variable without a name")
   }
  } else {
   newDefs = append(newDefs, def)
  }
 }
 f.tree.Defs = newDefs

 if len(found) == 0 {
  return nil
 }

 return runPatchListMod(func(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
  if mod.Type != "soong_config_module_type" {
   return nil
  }

  variables, ok := getLiteralListProperty(mod, "variables")
  if !ok {
   return nil
  }

  boolValues := strings.Builder{}
  empty := true
  for _, item := range variables.Values {
   nameValue, ok := item.(*parser.String)
   if !ok {
    empty = false
    continue
   }
   if found[nameValue.Value] {
    patchList.Add(item.Pos().Offset, item.End().Offset+2, "")

    boolValues.WriteString(`"`)
    boolValues.WriteString(nameValue.Value)
    boolValues.WriteString(`",`)
   } else {
    empty = false
   }
  }
  if empty {
   *patchList = parser.PatchList{}

   prop, _ := mod.GetProperty("variables")
   patchList.Add(prop.Pos().Offset, prop.End().Offset+2, "")
  }
  if boolValues.Len() == 0 {
   return nil
  }

  boolVariables, ok := getLiteralListProperty(mod, "bool_variables")
  if ok {
   patchList.Add(boolVariables.RBracePos.Offset, boolVariables.RBracePos.Offset, ","+boolValues.String())
  } else {
   patchList.Add(variables.RBracePos.Offset+2, variables.RBracePos.Offset+2,
    fmt.Sprintf(`bool_variables: [%s],`, boolValues.String()))
  }

  return nil
 })(f)

 return nil
}

func removeResourceAndAssetsIfDefault(f *Fixer) error {
 for _, def := range f.tree.Defs {
  mod, ok := def.(*parser.Module)
  if !ok {
   continue
  }
  resourceDirList, resourceDirFound := getLiteralListPropertyValue(mod, "resource_dirs")
  if resourceDirFound && len(resourceDirList) == 1 && resourceDirList[0] == "res" {
   removeProperty(mod, "resource_dirs")
  }
  assetDirList, assetDirFound := getLiteralListPropertyValue(mod, "asset_dirs")
  if assetDirFound && len(assetDirList) == 1 && assetDirList[0] == "assets" {
   removeProperty(mod, "asset_dirs")
  }
 }
 return nil
}

// Converts the default source list property, 'srcs', to a single source property with a given name.
// "LOCAL_MODULE" reference is also resolved during the conversion process.
func convertToSingleSource(mod *parser.Module, srcPropertyName string) {
 if srcs, ok := mod.GetProperty("srcs"); ok {
  if srcList, ok := srcs.Value.(*parser.List); ok {
   removeProperty(mod, "srcs")
   if len(srcList.Values) == 1 {
    mod.Properties = append(mod.Properties,
     &parser.Property{
      Name:     srcPropertyName,
      NamePos:  srcs.NamePos,
      ColonPos: srcs.ColonPos,
      Value:    resolveLocalModule(mod, srcList.Values[0])})
   } else if len(srcList.Values) > 1 {
    indicateAttributeError(mod, srcPropertyName, "LOCAL_SRC_FILES should contain at most one item")
   }
  } else if _, ok = srcs.Value.(*parser.Variable); ok {
   removeProperty(mod, "srcs")
   mod.Properties = append(mod.Properties,
    &parser.Property{Name: srcPropertyName,
     NamePos:  srcs.NamePos,
     ColonPos: srcs.ColonPos,
     Value:    resolveLocalModule(mod, srcs.Value)})
  } else {
   renameProperty(mod, "srcs", "apk")
  }
 }
}

type patchListModFunction func(*parser.Module, []byte, *parser.PatchList) error

func runPatchListMod(modFunc patchListModFunction) func(*Fixer) error {
 return func(f *Fixer) error {
  // Make sure all the offsets are accurate
  buf, err := f.reparse()
  if err != nil {
   return err
  }

  var patchlist parser.PatchList
  for _, def := range f.tree.Defs {
   mod, ok := def.(*parser.Module)
   if !ok {
    continue
   }

   err := modFunc(mod, buf, &patchlist)
   if err != nil {
    return err
   }
  }

  newBuf := new(bytes.Buffer)
  err = patchlist.Apply(bytes.NewReader(buf), newBuf)
  if err != nil {
   return err
  }

  // Save a copy of the buffer to print for errors below
  bufCopy := append([]byte(nil), newBuf.Bytes()...)

  newTree, err := parse(f.tree.Name, newBuf)
  if err != nil {
   return fmt.Errorf("Failed to parse: %v\nBuffer:\n%s", err, string(bufCopy))
  }

  f.tree = newTree

  return nil
 }
}

var commonPropertyPriorities = []string{
 "name",
 "defaults",
 "device_supported",
 "host_supported",
 "installable",
}

func reorderCommonProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
 if len(mod.Properties) == 0 {
  return nil
 }

 pos := mod.LBracePos.Offset + 1
 stage := ""

 for _, name := range commonPropertyPriorities {
  idx := propertyIndex(mod.Properties, name)
  if idx == -1 {
   continue
  }
  if idx == 0 {
   err := patchlist.Add(pos, pos, stage)
   if err != nil {
    return err
   }
   stage = ""

   pos = mod.Properties[0].End().Offset + 1
   mod.Properties = mod.Properties[1:]
   continue
  }

  prop := mod.Properties[idx]
  mod.Properties = append(mod.Properties[:idx], mod.Properties[idx+1:]...)

  stage += string(buf[prop.Pos().Offset : prop.End().Offset+1])

  err := patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, "")
  if err != nil {
   return err
  }
 }

 if stage != "" {
  err := patchlist.Add(pos, pos, stage)
  if err != nil {
   return err
  }
 }

 return nil
}

func removeTags(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
 prop, ok := mod.GetProperty("tags")
 if !ok {
  return nil
 }
 list, ok := prop.Value.(*parser.List)
 if !ok {
  return nil
 }

 replaceStr := ""

 for _, item := range list.Values {
  str, ok := item.(*parser.String)
  if !ok {
   replaceStr += fmt.Sprintf("// ERROR: Unable to parse tag %q\n", item)
   continue
  }

  switch str.Value {
  case "optional":
   continue
  case "debug":
   replaceStr += `// WARNING: Module tags are not supported in Soong.
    // Add this module to PRODUCT_PACKAGES_DEBUG in your product file if you want to
    // force installation for -userdebug and -eng builds.
    `
  case "eng":
   replaceStr += `// WARNING: Module tags are not supported in Soong.
    // Add this module to PRODUCT_PACKAGES_ENG in your product file if you want to
    // force installation for -eng builds.
    `
  case "tests":
   switch {
   case strings.Contains(mod.Type, "cc_test"),
    strings.Contains(mod.Type, "cc_library_static"),
    strings.Contains(mod.Type, "java_test"),
    mod.Type == "android_test",
    mod.Type == "android_test_import":
    continue
   case strings.Contains(mod.Type, "cc_lib"):
    replaceStr += `// WARNING: Module tags are not supported in Soong.
     // To make a shared library only for tests, use the "cc_test_library" module
     // type. If you don't use gtest, set "gtest: false".
     `
   case strings.Contains(mod.Type, "cc_bin"):
    replaceStr += `// WARNING: Module tags are not supported in Soong.
     // For native test binaries, use the "cc_test" module type. Some differences:
     //  - If you don't use gtest, set "gtest: false"
     //  - Binaries will be installed into /data/nativetest[64]/<name>/<name>
     //  - Both 32 & 64 bit versions will be built (as appropriate)
     `
   case strings.Contains(mod.Type, "java_lib"):
    replaceStr += `// WARNING: Module tags are not supported in Soong.
     // For JUnit or similar tests, use the "java_test" module type. A dependency on
     // Junit will be added by default, if it is using some other runner, set "junit: false".
     `
   case mod.Type == "android_app":
    replaceStr += `// WARNING: Module tags are not supported in Soong.
     // For JUnit or instrumentataion app tests, use the "android_test" module type.
     `
   default:
    replaceStr += `// WARNING: Module tags are not supported in Soong.
     // In most cases, tests are now identified by their module type:
     // cc_test, java_test, python_test
     `
   }
  default:
   replaceStr += fmt.Sprintf("// WARNING: Unknown module tag %q\n", str.Value)
  }
 }

 return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, replaceStr)
}

type propertyProvider interface {
 GetProperty(string) (*parser.Property, bool)
 RemoveProperty(string) bool
}

func removeNestedProperty(mod *parser.Module, patchList *parser.PatchList, propName string) error {
 propNames := strings.Split(propName, ".")

 var propProvider, toRemoveFrom propertyProvider
 propProvider = mod

 var propToRemove *parser.Property
 for i, name := range propNames {
  p, ok := propProvider.GetProperty(name)
  if !ok {
   return nil
  }
  // if this is the inner most element, it's time to delete
  if i == len(propNames)-1 {
   if propToRemove == nil {
    // if we cannot remove the properties that the current property is nested in,
    // remove only the current property
    propToRemove = p
    toRemoveFrom = propProvider
   }

   // remove the property from the list, in case we remove other properties in this list
   toRemoveFrom.RemoveProperty(propToRemove.Name)
   // only removing the property would leave blank line(s), remove with a patch
   if err := patchList.Add(propToRemove.Pos().Offset, propToRemove.End().Offset+2, ""); err != nil {
    return err
   }
  } else {
   propMap, ok := p.Value.(*parser.Map)
   if !ok {
    return nil
   }
   if len(propMap.Properties) > 1 {
    // if there are other properties in this struct, we need to keep this struct
    toRemoveFrom = nil
    propToRemove = nil
   } else if propToRemove == nil {
    // otherwise, we can remove the empty struct entirely
    toRemoveFrom = propProvider
    propToRemove = p
   }
   propProvider = propMap
  }
 }

 return nil
}

func removeObsoleteProperty(propName string) patchListModFunction {
 return func(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
  return removeNestedProperty(mod, patchList, propName)
 }
}

func mergeMatchingModuleProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
 return mergeMatchingProperties(&mod.Properties, buf, patchlist)
}

func mergeMatchingProperties(properties *[]*parser.Property, buf []byte, patchlist *parser.PatchList) error {
 seen := make(map[string]*parser.Property)
 for i := 0; i < len(*properties); i++ {
  property := (*properties)[i]
  if prev, exists := seen[property.Name]; exists {
   err := mergeProperties(prev, property, buf, patchlist)
   if err != nil {
    return err
   }
   *properties = append((*properties)[:i], (*properties)[i+1:]...)
  } else {
   seen[property.Name] = property
   if mapProperty, ok := property.Value.(*parser.Map); ok {
    err := mergeMatchingProperties(&mapProperty.Properties, buf, patchlist)
    if err != nil {
     return err
    }
   }
  }
 }
 return nil
}

func mergeProperties(a, b *parser.Property, buf []byte, patchlist *parser.PatchList) error {
 // The value of one of the properties may be a variable reference with no type assigned
 // Bail out in this case. Soong will notice duplicate entries and will tell to merge them.
 if _, isVar := a.Value.(*parser.Variable); isVar {
  return nil
 }
 if _, isVar := b.Value.(*parser.Variable); isVar {
  return nil
 }
 if a.Value.Type() != b.Value.Type() {
  return fmt.Errorf("type mismatch when merging properties %q: %s and %s", a.Name, a.Value.Type(), b.Value.Type())
 }

 switch a.Value.Type() {
 case parser.StringType:
  return fmt.Errorf("conflicting definitions of string property %q", a.Name)
 case parser.ListType:
  return mergeListProperties(a, b, buf, patchlist)
 }

 return nil
}

func mergeListProperties(a, b *parser.Property, buf []byte, patchlist *parser.PatchList) error {
 aval, oka := a.Value.(*parser.List)
 bval, okb := b.Value.(*parser.List)
 if !oka || !okb {
  // Merging expressions not supported yet
  return nil
 }

 s := string(buf[bval.LBracePos.Offset+1 : bval.RBracePos.Offset])
 if bval.LBracePos.Line != bval.RBracePos.Line {
  if s[0] != '\n' {
   panic("expected \n")
  }
  // If B is a multi line list, skip the first "\n" in case A already has a trailing "\n"
  s = s[1:]
 }
 if aval.LBracePos.Line == aval.RBracePos.Line {
  // A is a single line list with no trailing comma
  if len(aval.Values) > 0 {
   s = "," + s
  }
 }

 err := patchlist.Add(aval.RBracePos.Offset, aval.RBracePos.Offset, s)
 if err != nil {
  return err
 }
 err = patchlist.Add(b.NamePos.Offset, b.End().Offset+2, "")
 if err != nil {
  return err
 }

 return nil
}

// removes from <items> every item present in <removals>
func filterExpressionList(patchList *parser.PatchList, items *parser.List, removals *parser.List) {
 writeIndex := 0
 for _, item := range items.Values {
  included := true
  for _, removal := range removals.Values {
   equal, err := parser.ExpressionsAreSame(item, removal)
   if err != nil {
    continue
   }
   if equal {
    included = false
    break
   }
  }
  if included {
   items.Values[writeIndex] = item
   writeIndex++
  } else {
   patchList.Add(item.Pos().Offset, item.End().Offset+2, "")
  }
 }
 items.Values = items.Values[:writeIndex]
}

// Remove each modules[i].Properties[<legacyName>][j] that matches a modules[i].Properties[<canonicalName>][k]
func removeMatchingModuleListProperties(mod *parser.Module, patchList *parser.PatchList, canonicalName string, legacyName string) error {
 legacyProp, ok := mod.GetProperty(legacyName)
 if !ok {
  return nil
 }
 legacyList, ok := legacyProp.Value.(*parser.List)
 if !ok || len(legacyList.Values) == 0 {
  return nil
 }
 canonicalList, ok := getLiteralListProperty(mod, canonicalName)
 if !ok {
  return nil
 }

 localPatches := parser.PatchList{}
 filterExpressionList(&localPatches, legacyList, canonicalList)

 if len(legacyList.Values) == 0 {
  patchList.Add(legacyProp.Pos().Offset, legacyProp.End().Offset+2, "")
 } else {
  for _, p := range localPatches {
   patchList.Add(p.Start, p.End, p.Replacement)
  }
 }

 return nil
}

func hasNonEmptyLiteralListProperty(mod *parser.Module, name string) bool {
 list, found := getLiteralListProperty(mod, name)
 return found && len(list.Values) > 0
}

func hasNonEmptyLiteralStringProperty(mod *parser.Module, name string) bool {
 s, found := getLiteralStringPropertyValue(mod, name)
 return found && len(s) > 0
}

func getLiteralListProperty(mod *parser.Module, name string) (list *parser.List, found bool) {
 prop, ok := mod.GetProperty(name)
 if !ok {
  return nil, false
 }
 list, ok = prop.Value.(*parser.List)
 return list, ok
}

func getLiteralListPropertyValue(mod *parser.Module, name string) (list []string, found bool) {
 listValue, ok := getLiteralListProperty(mod, name)
 if !ok {
  return nil, false
 }
 for _, v := range listValue.Values {
  stringValue, ok := v.(*parser.String)
  if !ok {
   return nil, false
  }
  list = append(list, stringValue.Value)
 }

 return list, true
}

func getLiteralStringProperty(mod *parser.Module, name string) (s *parser.String, found bool) {
 prop, ok := mod.GetProperty(name)
 if !ok {
  return nil, false
 }
 s, ok = prop.Value.(*parser.String)
 return s, ok
}

func getLiteralStringPropertyValue(mod *parser.Module, name string) (s string, found bool) {
 stringValue, ok := getLiteralStringProperty(mod, name)
 if !ok {
  return "", false
 }

 return stringValue.Value, true
}

func getLiteralBoolProperty(mod *parser.Module, name string) (b *parser.Bool, found bool) {
 prop, ok := mod.GetProperty(name)
 if !ok {
  return nil, false
 }
 b, ok = prop.Value.(*parser.Bool)
 return b, ok
}

func getLiteralBoolPropertyValue(mod *parser.Module, name string) (s bool, found bool) {
 boolValue, ok := getLiteralBoolProperty(mod, name)
 if !ok {
  return false, false
 }

 return boolValue.Value, true
}

func propertyIndex(props []*parser.Property, propertyName string) int {
 for i, prop := range props {
  if prop.Name == propertyName {
   return i
  }
 }
 return -1
}

func renameProperty(mod *parser.Module, from, to string) {
 for _, prop := range mod.Properties {
  if prop.Name == from {
   prop.Name = to
  }
 }
}

func removeProperty(mod *parser.Module, propertyName string) {
 newList := make([]*parser.Property, 0, len(mod.Properties))
 for _, prop := range mod.Properties {
  if prop.Name != propertyName {
   newList = append(newList, prop)
  }
 }
 mod.Properties = newList
}

func inList(s string, list []string) bool {
 for _, v := range list {
  if s == v {
   return true
  }
 }
 return false
}

func formatFlagProperty(mod *parser.Module, field string, buf []byte, patchlist *parser.PatchList) error {
 // the comment or empty lines in the value of the field are skipped
 listValue, ok := getLiteralListProperty(mod, field)
 if !ok {
  // if do not find
  return nil
 }
 for i := 0; i < len(listValue.Values); i++ {
  curValue, ok := listValue.Values[i].(*parser.String)
  if !ok {
   return fmt.Errorf("Expecting string for %s.%s fields", mod.Type, field)
  }
  if !strings.HasPrefix(curValue.Value, "-") {
   return fmt.Errorf("Expecting the string `%s` starting with '-'", curValue.Value)
  }
  if i+1 < len(listValue.Values) {
   nextValue, ok := listValue.Values[i+1].(*parser.String)
   if !ok {
    return fmt.Errorf("Expecting string for %s.%s fields", mod.Type, field)
   }
   if !strings.HasPrefix(nextValue.Value, "-") {
    // delete the line
    err := patchlist.Add(curValue.Pos().Offset, curValue.End().Offset+2, "")
    if err != nil {
     return err
    }
    // replace the line
    value := "\"" + curValue.Value + " " + nextValue.Value + "\","
    err = patchlist.Add(nextValue.Pos().Offset, nextValue.End().Offset+1, value)
    if err != nil {
     return err
    }
    // combined two lines to one
    i++
   }
  }
 }
 return nil
}

func formatFlagProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
 relevantFields := []string{
  // cc flags
  "asflags",
  "cflags",
  "clang_asflags",
  "clang_cflags",
  "conlyflags",
  "cppflags",
  "ldflags",
  "tidy_flags",
  // java flags
  "aaptflags",
  "dxflags",
  "javacflags",
  "kotlincflags",
 }
 for _, field := range relevantFields {
  err := formatFlagProperty(mod, field, buf, patchlist)
  if err != nil {
   return err
  }
 }
 return nil
}

func rewriteLicenseProperty(fs pathtools.FileSystem, relativePath string) patchListModFunction {
 return func(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
  return rewriteLicenseProperties(mod, patchList, fs, relativePath)
 }
}

// rewrite the "android_license_kinds" and "android_license_files" properties to a package module
// (and a license module when needed).
func rewriteLicenseProperties(mod *parser.Module, patchList *parser.PatchList, fs pathtools.FileSystem,
 relativePath string) error {
 // if a package module has been added, no more action is needed.
 for _, patch := range *patchList {
  if strings.Contains(patch.Replacement, "package {") {
   return nil
  }
 }

 // initial the fs
 if fs == nil {
  fs = pathtools.NewOsFs(os.Getenv("ANDROID_BUILD_TOP"))
 }

 // initial the relativePath
 if len(relativePath) == 0 {
  relativePath = getModuleRelativePath()
 }
 // validate the relativePath
 ok := hasFile(relativePath+"/Android.mk", fs)
 // some modules in the existing test cases in the androidmk_test.go do not have a valid path
 if !ok && len(relativePath) > 0 {
  return fmt.Errorf("Cannot find an Android.mk file at path %q", relativePath)
 }

 licenseKindsPropertyName := "android_license_kinds"
 licenseFilesPropertyName := "android_license_files"

 androidBpFileErr := "// Error: No Android.bp file is found at path\n" +
  "// %s\n" +
  "// Please add one there with the needed license module first.\n" +
  "// Then reset the default_applicable_licenses property below with the license module name.\n"
 licenseModuleErr := "// Error: Cannot get the name of the license module in the\n" +
  "// %s file.\n" +
  "// If no such license module exists, please add one there first.\n" +
  "// Then reset the default_applicable_licenses property below with the license module name.\n"

 defaultApplicableLicense := "Android-Apache-2.0"
 var licenseModuleName, licensePatch string
 var hasFileInParentDir bool

 // when LOCAL_NOTICE_FILE is not empty
 if hasNonEmptyLiteralListProperty(mod, licenseFilesPropertyName) {
  hasFileInParentDir = hasValueStartWithTwoDotsLiteralList(mod, licenseFilesPropertyName)
  // if have LOCAL_NOTICE_FILE outside the current directory, need to find and refer to the license
  // module in the LOCAL_NOTICE_FILE location directly and no new license module needs to be created
  if hasFileInParentDir {
   bpPath, ok := getPathFromProperty(mod, licenseFilesPropertyName, fs, relativePath)
   if !ok {
    bpDir, err := getDirFromProperty(mod, licenseFilesPropertyName, fs, relativePath)
    if err != nil {
     return err
    }
    licensePatch += fmt.Sprintf(androidBpFileErr, bpDir)
    defaultApplicableLicense = ""
   } else {
    licenseModuleName, _ = getModuleName(bpPath, "license", fs)
    if len(licenseModuleName) == 0 {
     licensePatch += fmt.Sprintf(licenseModuleErr, bpPath)
    }
    defaultApplicableLicense = licenseModuleName
   }
  } else {
   // if have LOCAL_NOTICE_FILE in the current directory, need to create a new license module
   if len(relativePath) == 0 {
    return fmt.Errorf("Cannot obtain the relative path of the Android.mk file")
   }
   licenseModuleName = strings.Replace(relativePath, "/", "_", -1) + "_license"
   defaultApplicableLicense = licenseModuleName
  }
 }

 //add the package module
 if hasNonEmptyLiteralListProperty(mod, licenseKindsPropertyName) {
  licensePatch += "package {\n" +
   "    // See: http://go/android-license-faq\n" +
   "    default_applicable_licenses: [\n" +
   "         \"" + defaultApplicableLicense + "\",\n" +
   "    ],\n" +
   "}\n" +
   "\n"
 }

 // append the license module when necessary
 // when LOCAL_NOTICE_FILE is not empty and in the current directory, create a new license module
 // otherwise, use the above default license directly
 if hasNonEmptyLiteralListProperty(mod, licenseFilesPropertyName) && !hasFileInParentDir {
  licenseKinds, err := mergeLiteralListPropertyValue(mod, licenseKindsPropertyName)
  if err != nil {
   return err
  }
  licenseFiles, err := mergeLiteralListPropertyValue(mod, licenseFilesPropertyName)
  if err != nil {
   return err
  }
  licensePatch += "license {\n" +
   "    name: \"" + licenseModuleName + "\",\n" +
   "    visibility: [\":__subpackages__\"],\n" +
   "    license_kinds: [\n" +
   licenseKinds +
   "    ],\n" +
   "    license_text: [\n" +
   licenseFiles +
   "    ],\n" +
   "}\n" +
   "\n"
 }

 // add to the patchList
 pos := mod.Pos().Offset
 err := patchList.Add(pos, pos, licensePatch)
 if err != nil {
  return err
 }
 return nil
}

// merge the string vaules in a list property of a module into one string with expected format
func mergeLiteralListPropertyValue(mod *parser.Module, property string) (s string, err error) {
 listValue, ok := getLiteralListPropertyValue(mod, property)
 if !ok {
  // if do not find
  return "", fmt.Errorf("Cannot retrieve the %s.%s field", mod.Type, property)
 }
 for i := 0; i < len(listValue); i++ {
  s += "         \"" + listValue[i] + "\",\n"
 }
 return s, nil
}

// check whether a string list property has any value starting with `../`
func hasValueStartWithTwoDotsLiteralList(mod *parser.Module, property string) bool {
 listValue, ok := getLiteralListPropertyValue(mod, property)
 if ok {
  for i := 0; i < len(listValue); i++ {
   if strings.HasPrefix(listValue[i], "../") {
    return true
   }
  }
 }
 return false
}

// get the relative path from ANDROID_BUILD_TOP to the Android.mk file to be converted
func getModuleRelativePath() string {
 // get the absolute path of the top of the tree
 rootPath := os.Getenv("ANDROID_BUILD_TOP")
 // get the absolute path of the `Android.mk` file to be converted
 absPath := getModuleAbsolutePath()
 // get the relative path of the `Android.mk` file to top of the tree
 relModulePath, err := filepath.Rel(rootPath, absPath)
 if err != nil {
  return ""
 }
 return relModulePath
}

// get the absolute path of the Android.mk file to be converted
func getModuleAbsolutePath() string {
 // get the absolute path at where the `androidmk` commend is executed
 curAbsPath, err := filepath.Abs(".")
 if err != nil {
  return ""
 }
 // the argument for the `androidmk` command could be
 // 1. "./a/b/c/Android.mk"; 2. "a/b/c/Android.mk"; 3. "Android.mk"
 argPath := flag.Arg(0)
 if strings.HasPrefix(argPath, "./") {
  argPath = strings.TrimPrefix(argPath, ".")
 }
 argPath = strings.TrimSuffix(argPath, "Android.mk")
 if strings.HasSuffix(argPath, "/") {
  argPath = strings.TrimSuffix(argPath, "/")
 }
 if len(argPath) > 0 && !strings.HasPrefix(argPath, "/") {
  argPath = "/" + argPath
 }
 // get the absolute path of the `Android.mk` file to be converted
 absPath := curAbsPath + argPath
 return absPath
}

// check whether a file exists in a filesystem
func hasFile(path string, fs pathtools.FileSystem) bool {
 ok, _, _ := fs.Exists(path)
 return ok
}

// get the directory where an `Android.bp` file and the property files are expected to locate
func getDirFromProperty(mod *parser.Module, property string, fs pathtools.FileSystem, relativePath string) (string, error) {
 listValue, ok := getLiteralListPropertyValue(mod, property)
 if !ok {
  // if do not find
  return "", fmt.Errorf("Cannot retrieve the %s.%s property", mod.Type, property)
 }
 if len(listValue) == 0 {
  // if empty
  return "", fmt.Errorf("Cannot find the value of the %s.%s property", mod.Type, property)
 }
 if relativePath == "" {
  relativePath = "."
 }
 _, isDir, _ := fs.Exists(relativePath)
 if !isDir {
  return "", fmt.Errorf("Cannot find the path %q", relativePath)
 }
 path := relativePath
 for {
  if !strings.HasPrefix(listValue[0], "../") {
   break
  }
  path = filepath.Dir(path)
  listValue[0] = strings.TrimPrefix(listValue[0], "../")
 }
 _, isDir, _ = fs.Exists(path)
 if !isDir {
  return "", fmt.Errorf("Cannot find the path %q", path)
 }
 return path, nil
}

// get the path of the `Android.bp` file at the expected location where the property files locate
func getPathFromProperty(mod *parser.Module, property string, fs pathtools.FileSystem, relativePath string) (string, bool) {
 dir, err := getDirFromProperty(mod, property, fs, relativePath)
 if err != nil {
  return "", false
 }
 ok := hasFile(dir+"/Android.bp", fs)
 if !ok {
  return "", false
 }
 return dir + "/Android.bp", true
}

// parse an Android.bp file to get the name of the first module with type of moduleType
func getModuleName(path string, moduleType string, fs pathtools.FileSystem) (string, error) {
 tree, err := parserPath(path, fs)
 if err != nil {
  return "", err
 }
 for _, def := range tree.Defs {
  mod, ok := def.(*parser.Module)
  if !ok || mod.Type != moduleType {
   continue
  }
  prop, ok := mod.GetProperty("name")
  if !ok {
   return "", fmt.Errorf("Cannot get the %s."+"name property", mod.Type)
  }
  propVal, ok := prop.Value.(*parser.String)
  if ok {
   return propVal.Value, nil
  }
 }
 return "", fmt.Errorf("Cannot find the value of the %s."+"name property", moduleType)
}

// parse an Android.bp file with the specific path
func parserPath(path string, fs pathtools.FileSystem) (tree *parser.File, err error) {
 f, err := fs.Open(path)
 if err != nil {
  return tree, err
 }
 defer f.Close()
 fileContent, _ := ioutil.ReadAll(f)
 tree, err = parse(path, bytes.NewBufferString(string(fileContent)))
 if err != nil {
  return tree, err
 }
 return tree, nil
}

// remove the incorrect property that Soong does not support
func removeIncorrectProperties(propName string) patchListModFunction {
 return removeObsoleteProperty(propName)
}

// the modules on the same Android.mk file are expected to have the same license
func haveSameLicense(f *Fixer) error {
 androidLicenseProperties := []string{
  "android_license_kinds",
  "android_license_conditions",
  "android_license_files",
 }

 var prevModuleName string
 var prevLicenseKindsVals, prevLicenseConditionsVals, prevLicenseFilesVals []string
 prevLicenseVals := [][]string{
  prevLicenseKindsVals,
  prevLicenseConditionsVals,
  prevLicenseFilesVals,
 }

 for _, def := range f.tree.Defs {
  mod, ok := def.(*parser.Module)
  if !ok {
   continue
  }
  for idx, property := range androidLicenseProperties {
   curModuleName, ok := getLiteralStringPropertyValue(mod, "name")
   // some modules in the existing test cases in the androidmk_test.go do not have name property
   hasNameProperty := hasProperty(mod, "name")
   if hasNameProperty && (!ok || len(curModuleName) == 0) {
    return fmt.Errorf("Cannot retrieve the name property of a module of %s type.", mod.Type)
   }
   curVals, ok := getLiteralListPropertyValue(mod, property)
   // some modules in the existing test cases in the androidmk_test.go do not have license-related property
   hasLicenseProperty := hasProperty(mod, property)
   if hasLicenseProperty && (!ok || len(curVals) == 0) {
    // if do not find the property, or no value is found for the property
    return fmt.Errorf("Cannot retrieve the %s.%s property", mod.Type, property)
   }
   if len(prevLicenseVals[idx]) > 0 {
    if !reflect.DeepEqual(prevLicenseVals[idx], curVals) {
     return fmt.Errorf("Modules %s and %s are expected to have the same %s property.",
      prevModuleName, curModuleName, property)
    }
   }
   sort.Strings(curVals)
   prevLicenseVals[idx] = curVals
   prevModuleName = curModuleName
  }
 }
 return nil
}

func hasProperty(mod *parser.Module, propName string) bool {
 _, ok := mod.GetProperty(propName)
 return ok
}

[Dauer der Verarbeitung: 0.35 Sekunden, vorverarbeitet 2026-06-28]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik