merger/merger.go
func MergeFile(oldFile *rule.File, emptyRules, genRules []*rule.Rule, phase Phase, kinds map[string]rule.KindInfo) {
	getMergeAttrs := func(r *rule.Rule) map[string]bool {
		if phase == PreResolve {
			return kinds[r.Kind()].MergeableAttrs
		} else {
			return kinds[r.Kind()].ResolveAttrs
		}
	}

	// Merge empty rules into the file and delete any rules which become empty.
	for _, emptyRule := range emptyRules {
		if oldRule, _ := Match(oldFile.Rules, emptyRule, kinds[emptyRule.Kind()]); oldRule != nil {
			if oldRule.ShouldKeep() {
				continue
			}
			rule.MergeRules(emptyRule, oldRule, getMergeAttrs(emptyRule), oldFile.Path)
			if oldRule.IsEmpty(kinds[oldRule.Kind()]) {
				oldRule.Delete()
			}
		}
	}
	oldFile.Sync()

	// Match generated rules with existing rules in the file. Keep track of
	// rules with non-standard names.
	matchRules := make([]*rule.Rule, len(genRules))
	matchErrors := make([]error, len(genRules))
	substitutions := make(map[string]string)
	for i, genRule := range genRules {
		oldRule, err := Match(oldFile.Rules, genRule, kinds[genRule.Kind()])
		if err != nil {
			// TODO(jayconrod): add a verbose mode and log errors. They are too chatty
			// to print by default.
			matchErrors[i] = err
			continue
		}
		matchRules[i] = oldRule
		if oldRule != nil {
			if oldRule.Name() != genRule.Name() {
				substitutions[genRule.Name()] = oldRule.Name()
			}
		}
	}

	// Rename labels in generated rules that refer to other generated rules.
	if len(substitutions) > 0 {
		for _, genRule := range genRules {
			substituteRule(genRule, substitutions, kinds[genRule.Kind()])
		}
	}

	// Merge generated rules with existing rules or append to the end of the file.
	for i, genRule := range genRules {
		if matchErrors[i] != nil {
			continue
		}
		if matchRules[i] == nil {
			genRule.Insert(oldFile)
		} else {
			rule.MergeRules(genRule, matchRules[i], getMergeAttrs(genRule), oldFile.Path)
		}
	}
}
func MergeFile(oldFile *rule.File, emptyRules, genRules []*rule.Rule, phase Phase, kinds map[string]rule.KindInfo) {
	getMergeAttrs := func(r *rule.Rule) map[string]bool {
		if phase == PreResolve {
			return kinds[r.Kind()].MergeableAttrs
		} else {
			return kinds[r.Kind()].ResolveAttrs
		}
	}

	// Merge empty rules into the file and delete any rules which become empty.
	for _, emptyRule := range emptyRules {
		if oldRule, _ := Match(oldFile.Rules, emptyRule, kinds[emptyRule.Kind()]); oldRule != nil {
			if oldRule.ShouldKeep() {
				continue
			}
			rule.MergeRules(emptyRule, oldRule, getMergeAttrs(emptyRule), oldFile.Path)
			if oldRule.IsEmpty(kinds[oldRule.Kind()]) {
				oldRule.Delete()
			}
		}
	}
	oldFile.Sync()

	// Match generated rules with existing rules in the file. Keep track of
	// rules with non-standard names.
	matchRules := make([]*rule.Rule, len(genRules))
	matchErrors := make([]error, len(genRules))
	substitutions := make(map[string]string)
	for i, genRule := range genRules {
		oldRule, err := Match(oldFile.Rules, genRule, kinds[genRule.Kind()])
		if err != nil {
			// TODO(jayconrod): add a verbose mode and log errors. They are too chatty
			// to print by default.
			matchErrors[i] = err
			continue
		}
		matchRules[i] = oldRule
		if oldRule != nil {
			if oldRule.Name() != genRule.Name() {
				substitutions[genRule.Name()] = oldRule.Name()
			}
		}
	}

	// Rename labels in generated rules that refer to other generated rules.
	if len(substitutions) > 0 {
		for _, genRule := range genRules {
			substituteRule(genRule, substitutions, kinds[genRule.Kind()])
		}
	}

	// Merge generated rules with existing rules or append to the end of the file.
	for i, genRule := range genRules {
		if matchErrors[i] != nil {
			continue
		}
		if matchRules[i] == nil {
			genRule.Insert(oldFile)
		} else {
			rule.MergeRules(genRule, matchRules[i], getMergeAttrs(genRule), oldFile.Path)
		}
	}
}
// MergeFile combines information from newly generated rules with matching
// rules in an existing build file. MergeFile can also delete rules which
// are empty after merging.
//
// oldFile is the file to merge. It must not be nil.
//
// emptyRules is a list of stub rules (with no attributes other than name)
// which were not generated. These are merged with matching rules. The merged
// rules are deleted if they contain no attributes that make them buildable
// (e.g., srcs, deps, anything in rule.KindInfo.NonEmptyAttrs).
//
// genRules is a list of newly generated rules. These are merged with
// matching rules. A rule matches if it has the same kind and name or if
// some other attribute in rule.KindInfo.MatchAttrs matches (e.g.,
// "importpath" in go_library). Elements of genRules that don't match
// any existing rule are appended to the end of oldFile.
//
// phase indicates whether this is a pre- or post-resolve merge. Different
// attributes (rule.KindInfo.MergeableAttrs or ResolveAttrs) will be merged.
//
// kinds maps rule kinds (e.g., "go_library") to metadata that helps merge
// rules of that kind.
//
// When a generated and existing rule are merged, each attribute is merged
// separately. If an attribute is mergeable (according to KindInfo), values
// from the existing attribute are replaced by values from the generated
// attribute. Comments are preserved on values that are present in both
// versions of the attribute. If at attribute is not mergeable, the generated
// version of the attribute will be added if no existing attribute is present;
// otherwise, the existing attribute will be preserved.
//
// Note that "# keep" comments affect merging. If a value within an existing
// attribute is marked with a "# keep" comment, it will not be removed.
// If an attribute is marked with a "# keep" comment, it will not be merged.
// If a rule is marked with a "# keep" comment, the whole rule will not
// be modified.