github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/soong/androidmk/cmd/androidmk/androidmk.go (about)

     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  package main
    16  
    17  import (
    18  	"bytes"
    19  	"flag"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  	"strings"
    24  	"text/scanner"
    25  
    26  	"android/soong/bpfix/bpfix"
    27  
    28  	mkparser "android/soong/androidmk/parser"
    29  
    30  	bpparser "github.com/google/blueprint/parser"
    31  )
    32  
    33  var usage = func() {
    34  	fmt.Fprintf(os.Stderr, "usage: androidmk [flags] <inputFile>\n"+
    35  		"\nandroidmk parses <inputFile> as an Android.mk file and attempts to output an analogous Android.bp file (to standard out)\n")
    36  	flag.PrintDefaults()
    37  	os.Exit(1)
    38  }
    39  
    40  // TODO: non-expanded variables with expressions
    41  
    42  type bpFile struct {
    43  	comments          []*bpparser.CommentGroup
    44  	defs              []bpparser.Definition
    45  	localAssignments  map[string]*bpparser.Property
    46  	globalAssignments map[string]*bpparser.Expression
    47  	scope             mkparser.Scope
    48  	module            *bpparser.Module
    49  
    50  	mkPos scanner.Position // Position of the last handled line in the makefile
    51  	bpPos scanner.Position // Position of the last emitted line to the blueprint file
    52  
    53  	inModule bool
    54  }
    55  
    56  func (f *bpFile) insertComment(s string) {
    57  	f.comments = append(f.comments, &bpparser.CommentGroup{
    58  		Comments: []*bpparser.Comment{
    59  			&bpparser.Comment{
    60  				Comment: []string{s},
    61  				Slash:   f.bpPos,
    62  			},
    63  		},
    64  	})
    65  	f.bpPos.Offset += len(s)
    66  }
    67  
    68  func (f *bpFile) insertExtraComment(s string) {
    69  	f.insertComment(s)
    70  	f.bpPos.Line++
    71  }
    72  
    73  // records that the given node failed to be converted and includes an explanatory message
    74  func (f *bpFile) errorf(failedNode mkparser.Node, message string, args ...interface{}) {
    75  	orig := failedNode.Dump()
    76  	message = fmt.Sprintf(message, args...)
    77  	f.addErrorText(fmt.Sprintf("// ANDROIDMK TRANSLATION ERROR: %s", message))
    78  
    79  	lines := strings.Split(orig, "\n")
    80  	for _, l := range lines {
    81  		f.insertExtraComment("// " + l)
    82  	}
    83  }
    84  
    85  // records that something unexpected occurred
    86  func (f *bpFile) warnf(message string, args ...interface{}) {
    87  	message = fmt.Sprintf(message, args...)
    88  	f.addErrorText(fmt.Sprintf("// ANDROIDMK TRANSLATION WARNING: %s", message))
    89  }
    90  
    91  // adds the given error message as-is to the bottom of the (in-progress) file
    92  func (f *bpFile) addErrorText(message string) {
    93  	f.insertExtraComment(message)
    94  }
    95  
    96  func (f *bpFile) setMkPos(pos, end scanner.Position) {
    97  	// It is unusual but not forbidden for pos.Line to be smaller than f.mkPos.Line
    98  	// For example:
    99  	//
   100  	// if true                       # this line is emitted 1st
   101  	// if true                       # this line is emitted 2nd
   102  	// some-target: some-file        # this line is emitted 3rd
   103  	//         echo doing something  # this recipe is emitted 6th
   104  	// endif #some comment           # this endif is emitted 4th; this comment is part of the recipe
   105  	//         echo doing more stuff # this is part of the recipe
   106  	// endif                         # this endif is emitted 5th
   107  	//
   108  	// However, if pos.Line < f.mkPos.Line, we treat it as though it were equal
   109  	if pos.Line >= f.mkPos.Line {
   110  		f.bpPos.Line += (pos.Line - f.mkPos.Line)
   111  		f.mkPos = end
   112  	}
   113  
   114  }
   115  
   116  type conditional struct {
   117  	cond string
   118  	eq   bool
   119  }
   120  
   121  func main() {
   122  	flag.Usage = usage
   123  	flag.Parse()
   124  	if len(flag.Args()) != 1 {
   125  		usage()
   126  	}
   127  	filePathToRead := flag.Arg(0)
   128  	b, err := ioutil.ReadFile(filePathToRead)
   129  	if err != nil {
   130  		fmt.Println(err.Error())
   131  		return
   132  	}
   133  
   134  	output, errs := convertFile(os.Args[1], bytes.NewBuffer(b))
   135  	if len(errs) > 0 {
   136  		for _, err := range errs {
   137  			fmt.Fprintln(os.Stderr, "ERROR: ", err)
   138  		}
   139  		os.Exit(1)
   140  	}
   141  
   142  	fmt.Print(output)
   143  }
   144  
   145  func convertFile(filename string, buffer *bytes.Buffer) (string, []error) {
   146  	p := mkparser.NewParser(filename, buffer)
   147  
   148  	nodes, errs := p.Parse()
   149  	if len(errs) > 0 {
   150  		return "", errs
   151  	}
   152  
   153  	file := &bpFile{
   154  		scope:             androidScope(),
   155  		localAssignments:  make(map[string]*bpparser.Property),
   156  		globalAssignments: make(map[string]*bpparser.Expression),
   157  	}
   158  
   159  	var conds []*conditional
   160  	var assignmentCond *conditional
   161  
   162  	for _, node := range nodes {
   163  		file.setMkPos(p.Unpack(node.Pos()), p.Unpack(node.End()))
   164  
   165  		switch x := node.(type) {
   166  		case *mkparser.Comment:
   167  			file.insertComment("//" + x.Comment)
   168  		case *mkparser.Assignment:
   169  			handleAssignment(file, x, assignmentCond)
   170  		case *mkparser.Directive:
   171  			switch x.Name {
   172  			case "include":
   173  				val := x.Args.Value(file.scope)
   174  				switch {
   175  				case soongModuleTypes[val]:
   176  					handleModuleConditionals(file, x, conds)
   177  					makeModule(file, val)
   178  				case val == clear_vars:
   179  					resetModule(file)
   180  				case val == include_ignored:
   181  					// subdirs are already automatically included in Soong
   182  					continue
   183  				default:
   184  					file.errorf(x, "unsupported include")
   185  					continue
   186  				}
   187  			case "ifeq", "ifneq", "ifdef", "ifndef":
   188  				args := x.Args.Dump()
   189  				eq := x.Name == "ifeq" || x.Name == "ifdef"
   190  				if _, ok := conditionalTranslations[args]; ok {
   191  					newCond := conditional{args, eq}
   192  					conds = append(conds, &newCond)
   193  					if file.inModule {
   194  						if assignmentCond == nil {
   195  							assignmentCond = &newCond
   196  						} else {
   197  							file.errorf(x, "unsupported nested conditional in module")
   198  						}
   199  					}
   200  				} else {
   201  					file.errorf(x, "unsupported conditional")
   202  					conds = append(conds, nil)
   203  					continue
   204  				}
   205  			case "else":
   206  				if len(conds) == 0 {
   207  					file.errorf(x, "missing if before else")
   208  					continue
   209  				} else if conds[len(conds)-1] == nil {
   210  					file.errorf(x, "else from unsupported contitional")
   211  					continue
   212  				}
   213  				conds[len(conds)-1].eq = !conds[len(conds)-1].eq
   214  			case "endif":
   215  				if len(conds) == 0 {
   216  					file.errorf(x, "missing if before endif")
   217  					continue
   218  				} else if conds[len(conds)-1] == nil {
   219  					file.errorf(x, "endif from unsupported contitional")
   220  					conds = conds[:len(conds)-1]
   221  				} else {
   222  					if assignmentCond == conds[len(conds)-1] {
   223  						assignmentCond = nil
   224  					}
   225  					conds = conds[:len(conds)-1]
   226  				}
   227  			default:
   228  				file.errorf(x, "unsupported directive")
   229  				continue
   230  			}
   231  		default:
   232  			file.errorf(x, "unsupported line")
   233  		}
   234  	}
   235  
   236  	tree := &bpparser.File{
   237  		Defs:     file.defs,
   238  		Comments: file.comments,
   239  	}
   240  
   241  	// check for common supported but undesirable structures and clean them up
   242  	fixer := bpfix.NewFixer(tree)
   243  	tree, err := fixer.Fix(bpfix.NewFixRequest().AddAll())
   244  	if err != nil {
   245  		return "", []error{err}
   246  	}
   247  
   248  	out, err := bpparser.Print(tree)
   249  	if err != nil {
   250  		return "", []error{err}
   251  	}
   252  
   253  	return string(out), nil
   254  }
   255  
   256  func handleAssignment(file *bpFile, assignment *mkparser.Assignment, c *conditional) {
   257  	if !assignment.Name.Const() {
   258  		file.errorf(assignment, "unsupported non-const variable name")
   259  		return
   260  	}
   261  
   262  	if assignment.Target != nil {
   263  		file.errorf(assignment, "unsupported target assignment")
   264  		return
   265  	}
   266  
   267  	name := assignment.Name.Value(nil)
   268  	prefix := ""
   269  
   270  	if strings.HasPrefix(name, "LOCAL_") {
   271  		for _, x := range propertyPrefixes {
   272  			if strings.HasSuffix(name, "_"+x.mk) {
   273  				name = strings.TrimSuffix(name, "_"+x.mk)
   274  				prefix = x.bp
   275  				break
   276  			}
   277  		}
   278  
   279  		if c != nil {
   280  			if prefix != "" {
   281  				file.errorf(assignment, "prefix assignment inside conditional, skipping conditional")
   282  			} else {
   283  				var ok bool
   284  				if prefix, ok = conditionalTranslations[c.cond][c.eq]; !ok {
   285  					panic("unknown conditional")
   286  				}
   287  			}
   288  		}
   289  	} else {
   290  		if c != nil {
   291  			eq := "eq"
   292  			if !c.eq {
   293  				eq = "neq"
   294  			}
   295  			file.errorf(assignment, "conditional %s %s on global assignment", eq, c.cond)
   296  		}
   297  	}
   298  
   299  	appendVariable := assignment.Type == "+="
   300  
   301  	var err error
   302  	if prop, ok := rewriteProperties[name]; ok {
   303  		err = prop(variableAssignmentContext{file, prefix, assignment.Value, appendVariable})
   304  	} else {
   305  		switch {
   306  		case name == "LOCAL_ARM_MODE":
   307  			// This is a hack to get the LOCAL_ARM_MODE value inside
   308  			// of an arch: { arm: {} } block.
   309  			armModeAssign := assignment
   310  			armModeAssign.Name = mkparser.SimpleMakeString("LOCAL_ARM_MODE_HACK_arm", assignment.Name.Pos())
   311  			handleAssignment(file, armModeAssign, c)
   312  		case strings.HasPrefix(name, "LOCAL_"):
   313  			file.errorf(assignment, "unsupported assignment to %s", name)
   314  			return
   315  		default:
   316  			var val bpparser.Expression
   317  			val, err = makeVariableToBlueprint(file, assignment.Value, bpparser.ListType)
   318  			if err == nil {
   319  				err = setVariable(file, appendVariable, prefix, name, val, false)
   320  			}
   321  		}
   322  	}
   323  	if err != nil {
   324  		file.errorf(assignment, err.Error())
   325  	}
   326  }
   327  
   328  func handleModuleConditionals(file *bpFile, directive *mkparser.Directive, conds []*conditional) {
   329  	for _, c := range conds {
   330  		if c == nil {
   331  			continue
   332  		}
   333  
   334  		if _, ok := conditionalTranslations[c.cond]; !ok {
   335  			panic("unknown conditional " + c.cond)
   336  		}
   337  
   338  		disabledPrefix := conditionalTranslations[c.cond][!c.eq]
   339  
   340  		// Create a fake assignment with enabled = false
   341  		val, err := makeVariableToBlueprint(file, mkparser.SimpleMakeString("false", mkparser.NoPos), bpparser.BoolType)
   342  		if err == nil {
   343  			err = setVariable(file, false, disabledPrefix, "enabled", val, true)
   344  		}
   345  		if err != nil {
   346  			file.errorf(directive, err.Error())
   347  		}
   348  	}
   349  }
   350  
   351  func makeModule(file *bpFile, t string) {
   352  	file.module.Type = t
   353  	file.module.TypePos = file.module.LBracePos
   354  	file.module.RBracePos = file.bpPos
   355  	file.defs = append(file.defs, file.module)
   356  	file.inModule = false
   357  }
   358  
   359  func resetModule(file *bpFile) {
   360  	file.module = &bpparser.Module{}
   361  	file.module.LBracePos = file.bpPos
   362  	file.localAssignments = make(map[string]*bpparser.Property)
   363  	file.inModule = true
   364  }
   365  
   366  func makeVariableToBlueprint(file *bpFile, val *mkparser.MakeString,
   367  	typ bpparser.Type) (bpparser.Expression, error) {
   368  
   369  	var exp bpparser.Expression
   370  	var err error
   371  	switch typ {
   372  	case bpparser.ListType:
   373  		exp, err = makeToListExpression(val, file.scope)
   374  	case bpparser.StringType:
   375  		exp, err = makeToStringExpression(val, file.scope)
   376  	case bpparser.BoolType:
   377  		exp, err = makeToBoolExpression(val)
   378  	default:
   379  		panic("unknown type")
   380  	}
   381  
   382  	if err != nil {
   383  		return nil, err
   384  	}
   385  
   386  	return exp, nil
   387  }
   388  
   389  func setVariable(file *bpFile, plusequals bool, prefix, name string, value bpparser.Expression, local bool) error {
   390  
   391  	if prefix != "" {
   392  		name = prefix + "." + name
   393  	}
   394  
   395  	pos := file.bpPos
   396  
   397  	var oldValue *bpparser.Expression
   398  	if local {
   399  		oldProp := file.localAssignments[name]
   400  		if oldProp != nil {
   401  			oldValue = &oldProp.Value
   402  		}
   403  	} else {
   404  		oldValue = file.globalAssignments[name]
   405  	}
   406  
   407  	if local {
   408  		if oldValue != nil && plusequals {
   409  			val, err := addValues(*oldValue, value)
   410  			if err != nil {
   411  				return fmt.Errorf("unsupported addition: %s", err.Error())
   412  			}
   413  			val.(*bpparser.Operator).OperatorPos = pos
   414  			*oldValue = val
   415  		} else {
   416  			names := strings.Split(name, ".")
   417  			if file.module == nil {
   418  				file.warnf("No 'include $(CLEAR_VARS)' detected before first assignment; clearing vars now")
   419  				resetModule(file)
   420  			}
   421  			container := &file.module.Properties
   422  
   423  			for i, n := range names[:len(names)-1] {
   424  				fqn := strings.Join(names[0:i+1], ".")
   425  				prop := file.localAssignments[fqn]
   426  				if prop == nil {
   427  					prop = &bpparser.Property{
   428  						Name:    n,
   429  						NamePos: pos,
   430  						Value: &bpparser.Map{
   431  							Properties: []*bpparser.Property{},
   432  						},
   433  					}
   434  					file.localAssignments[fqn] = prop
   435  					*container = append(*container, prop)
   436  				}
   437  				container = &prop.Value.(*bpparser.Map).Properties
   438  			}
   439  
   440  			prop := &bpparser.Property{
   441  				Name:    names[len(names)-1],
   442  				NamePos: pos,
   443  				Value:   value,
   444  			}
   445  			file.localAssignments[name] = prop
   446  			*container = append(*container, prop)
   447  		}
   448  	} else {
   449  		if oldValue != nil && plusequals {
   450  			a := &bpparser.Assignment{
   451  				Name:      name,
   452  				NamePos:   pos,
   453  				Value:     value,
   454  				OrigValue: value,
   455  				EqualsPos: pos,
   456  				Assigner:  "+=",
   457  			}
   458  			file.defs = append(file.defs, a)
   459  		} else {
   460  			a := &bpparser.Assignment{
   461  				Name:      name,
   462  				NamePos:   pos,
   463  				Value:     value,
   464  				OrigValue: value,
   465  				EqualsPos: pos,
   466  				Assigner:  "=",
   467  			}
   468  			file.globalAssignments[name] = &a.Value
   469  			file.defs = append(file.defs, a)
   470  		}
   471  	}
   472  	return nil
   473  }