123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- // Copyright 2022 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.
- package main
- import (
- "flag"
- "fmt"
- "io/ioutil"
- "os"
- "strings"
- "android/soong/cmd/symbols_map/symbols_map_proto"
- "android/soong/response"
- "github.com/google/blueprint/pathtools"
- "google.golang.org/protobuf/encoding/prototext"
- "google.golang.org/protobuf/proto"
- )
- // This tool is used to extract a hash from an elf file or an r8 dictionary and store it as a
- // textproto, or to merge multiple textprotos together.
- func main() {
- var expandedArgs []string
- for _, arg := range os.Args[1:] {
- if strings.HasPrefix(arg, "@") {
- f, err := os.Open(strings.TrimPrefix(arg, "@"))
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(1)
- }
- respArgs, err := response.ReadRspFile(f)
- f.Close()
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(1)
- }
- expandedArgs = append(expandedArgs, respArgs...)
- } else {
- expandedArgs = append(expandedArgs, arg)
- }
- }
- flags := flag.NewFlagSet("flags", flag.ExitOnError)
- // Hide the flag package to prevent accidental references to flag instead of flags.
- flag := struct{}{}
- _ = flag
- flags.Usage = func() {
- fmt.Fprintf(flags.Output(), "Usage of %s:\n", os.Args[0])
- fmt.Fprintf(flags.Output(), " %s -elf|-r8 <input file> [-write_if_changed] <output file>\n", os.Args[0])
- fmt.Fprintf(flags.Output(), " %s -merge <output file> [-write_if_changed] [-ignore_missing_files] [-strip_prefix <prefix>] [<input file>...]\n", os.Args[0])
- fmt.Fprintln(flags.Output())
- flags.PrintDefaults()
- }
- elfFile := flags.String("elf", "", "extract identifier from an elf file")
- r8File := flags.String("r8", "", "extract identifier from an r8 dictionary")
- merge := flags.String("merge", "", "merge multiple identifier protos")
- writeIfChanged := flags.Bool("write_if_changed", false, "only write output file if it is modified")
- ignoreMissingFiles := flags.Bool("ignore_missing_files", false, "ignore missing input files in merge mode")
- stripPrefix := flags.String("strip_prefix", "", "prefix to strip off of the location field in merge mode")
- flags.Parse(expandedArgs)
- if *merge != "" {
- // If merge mode was requested perform the merge and exit early.
- err := mergeProtos(*merge, flags.Args(), *stripPrefix, *writeIfChanged, *ignoreMissingFiles)
- if err != nil {
- fmt.Fprintf(os.Stderr, "failed to merge protos: %s", err)
- os.Exit(1)
- }
- os.Exit(0)
- }
- if *elfFile == "" && *r8File == "" {
- fmt.Fprintf(os.Stderr, "-elf or -r8 argument is required\n")
- flags.Usage()
- os.Exit(1)
- }
- if *elfFile != "" && *r8File != "" {
- fmt.Fprintf(os.Stderr, "only one of -elf or -r8 argument is allowed\n")
- flags.Usage()
- os.Exit(1)
- }
- if flags.NArg() != 1 {
- flags.Usage()
- os.Exit(1)
- }
- output := flags.Arg(0)
- var identifier string
- var location string
- var typ symbols_map_proto.Mapping_Type
- var err error
- if *elfFile != "" {
- typ = symbols_map_proto.Mapping_ELF
- location = *elfFile
- identifier, err = elfIdentifier(*elfFile, true)
- if err != nil {
- fmt.Fprintf(os.Stderr, "error reading elf identifier: %s\n", err)
- os.Exit(1)
- }
- } else if *r8File != "" {
- typ = symbols_map_proto.Mapping_R8
- identifier, err = r8Identifier(*r8File)
- location = *r8File
- if err != nil {
- fmt.Fprintf(os.Stderr, "error reading r8 identifier: %s\n", err)
- os.Exit(1)
- }
- } else {
- panic("shouldn't get here")
- }
- mapping := symbols_map_proto.Mapping{
- Identifier: proto.String(identifier),
- Location: proto.String(location),
- Type: typ.Enum(),
- }
- err = writeTextProto(output, &mapping, *writeIfChanged)
- if err != nil {
- fmt.Fprintf(os.Stderr, "error writing output: %s\n", err)
- os.Exit(1)
- }
- }
- // writeTextProto writes a proto to an output file as a textproto, optionally leaving the file
- // unmodified if it was already up to date.
- func writeTextProto(output string, message proto.Message, writeIfChanged bool) error {
- marshaller := prototext.MarshalOptions{Multiline: true}
- data, err := marshaller.Marshal(message)
- if err != nil {
- return fmt.Errorf("error marshalling textproto: %w", err)
- }
- if writeIfChanged {
- err = pathtools.WriteFileIfChanged(output, data, 0666)
- } else {
- err = ioutil.WriteFile(output, data, 0666)
- }
- if err != nil {
- return fmt.Errorf("error writing to %s: %w\n", output, err)
- }
- return nil
- }
- // mergeProtos merges a list of textproto files containing Mapping messages into a single textproto
- // containing a Mappings message.
- func mergeProtos(output string, inputs []string, stripPrefix string, writeIfChanged bool, ignoreMissingFiles bool) error {
- mappings := symbols_map_proto.Mappings{}
- for _, input := range inputs {
- mapping := symbols_map_proto.Mapping{}
- data, err := ioutil.ReadFile(input)
- if err != nil {
- if ignoreMissingFiles && os.IsNotExist(err) {
- // Merge mode is used on a list of files in the packaging directory. If multiple
- // goals are included on the build command line, for example `dist` and `tests`,
- // then the symbols packaging rule for `dist` can run while a dependency of `tests`
- // is modifying the symbols packaging directory. That can result in a file that
- // existed when the file list was generated being deleted as part of updating it,
- // resulting in sporadic ENOENT errors. Ignore them if -ignore_missing_files
- // was passed on the command line.
- continue
- }
- return fmt.Errorf("failed to read %s: %w", input, err)
- }
- err = prototext.Unmarshal(data, &mapping)
- if err != nil {
- return fmt.Errorf("failed to parse textproto %s: %w", input, err)
- }
- if stripPrefix != "" && mapping.Location != nil {
- mapping.Location = proto.String(strings.TrimPrefix(*mapping.Location, stripPrefix))
- }
- mappings.Mappings = append(mappings.Mappings, &mapping)
- }
- return writeTextProto(output, &mappings, writeIfChanged)
- }
|