blob: bde1bcaf84c48e1a9a753a3b0a01f1240c0e5c76 [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// 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"
"path/filepath"
"reflect"
"sort"
"android.googlesource.com/platform/tools/gpu/framework/app"
"android.googlesource.com/platform/tools/gpu/framework/binary/cyclic"
"android.googlesource.com/platform/tools/gpu/framework/binary/endian"
"android.googlesource.com/platform/tools/gpu/framework/device"
"android.googlesource.com/platform/tools/gpu/framework/log"
"android.googlesource.com/platform/tools/gpu/framework/stringtable"
"android.googlesource.com/platform/tools/gpu/framework/stringtable/parser"
)
var (
def = flag.String("def", "", "The path to the Go string definition file")
pkg = flag.String("pkg", "", "The directory to hold the output string packages.")
)
func main() {
app.ShortHelp = "stringgen compiles string table files to string packages and a Go definition file."
app.Run(run)
}
func run(ctx log.Context) error {
tables := map[stringtable.Info]*stringtable.StringTable{}
for _, path := range flag.Args() {
content, err := ioutil.ReadFile(path)
if err != nil {
return err
}
table, errs := parser.Parse(path, string(content))
if len(errs) > 0 {
return fmt.Errorf("%s", errs)
}
if existing, exists := tables[table.Info]; exists {
// Merge tables, check for duplicates.
for k, v := range table.Entries {
if _, dup := existing.Entries[k]; !dup {
existing.Entries[k] = v
} else {
return ctx.S("Key", k).AsError("Duplicate string key found")
}
}
} else {
tables[table.Info] = table
}
}
if len(tables) == 0 {
return ctx.AsError("No string table files provided")
}
ctx.Printf("Found %d string table file(s)", len(tables))
if err := validate(ctx, tables); err != nil {
return err
}
if *pkg != "" {
if err := writePackages(tables, *pkg); err != nil {
return err
}
}
if *def != "" {
for _, t := range tables {
if err := writeDefinitions(t, *def); err != nil {
return err
}
break
}
}
return nil
}
// entry is a map of Info -> parameter list
type entry map[stringtable.Info][]string
func writePackages(tables map[stringtable.Info]*stringtable.StringTable, path string) error {
for info, table := range tables {
path := filepath.Join(path, info.CultureCode+".stb")
os.MkdirAll(filepath.Dir(path), 0755)
w, err := os.Create(path)
if err != nil {
return err
}
defer w.Close()
e := cyclic.Encoder(endian.Writer(w, device.LittleEndian))
e.Object(table)
if err := e.Error(); err != nil {
return err
}
}
return nil
}
func writeDefinitions(table *stringtable.StringTable, path string) error {
abspath, err := filepath.Abs(path)
if err != nil {
return err
}
os.MkdirAll(filepath.Dir(abspath), 0755)
w, err := os.Create(abspath)
if err != nil {
return err
}
defer w.Close()
entries := make(EntryList, 0, len(table.Entries))
for key, node := range table.Entries {
entry := Entry{
Key: key,
Parameters: params(node),
}
entries = append(entries, entry)
}
sort.Sort(entries)
_, pkg := filepath.Split(filepath.Dir(abspath))
return Execute(pkg, entries, w)
}
// checks for consistency between the various localizations of strings.
func validate(ctx log.Context, tables map[stringtable.Info]*stringtable.StringTable) error {
all := map[string]entry{}
for info, table := range tables {
for key, node := range table.Entries {
e := all[key]
if e == nil {
e = make(entry)
}
e[info] = params(node)
all[key] = e
}
}
for key, entry := range all {
ctx := ctx.S("Key", key)
if len(entry) != len(tables) {
for info := range tables {
if _, found := entry[info]; !found {
return ctx.V("Table", info).AsError("StringTable missing entry")
}
}
}
var firstInfo stringtable.Info
var firstParams []string
for info, params := range entry {
if firstParams == nil {
firstInfo, firstParams = info, params
} else {
if reflect.DeepEqual(firstParams, params) {
return ctx.V("First", firstInfo).V("Second", info).AsError("Parameter list different")
}
}
}
}
return nil
}
// params returns the list of parameters used by the stringtable node.
func params(n stringtable.Node) []string {
switch n := n.(type) {
case *stringtable.Block:
p := []string{}
for _, n := range n.Children {
p = append(p, params(n)...)
}
return p
case *stringtable.Parameter:
return []string{n.Key}
}
return nil
}