Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/build/build/blueprint/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 24 kB image not shown  

Quelle  package_ctx.go   Sprache: unbekannt

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

// Copyright 2014 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 blueprint

import (
 "errors"
 "fmt"
 "reflect"
 "regexp"
 "runtime"
 "strings"
 "sync"
)

// A PackageContext provides a way to create package-scoped Ninja pools,
// rules, and variables.  A Go package should create a single unexported
// package-scoped PackageContext variable that it uses to create all package-
// scoped Ninja object definitions.  This PackageContext object should then be
// passed to all calls to define module- or singleton-specific Ninja
// definitions.  For example:
//
// package blah
//
// import (
//     "blueprint"
// )
//
// var (
//     pctx = NewPackageContext("path/to/blah")
//
//     myPrivateVar = pctx.StaticVariable("myPrivateVar", "abcdef")
//     MyExportedVar = pctx.StaticVariable("MyExportedVar", "$myPrivateVar 123456!")
//
//     SomeRule = pctx.StaticRule(...)
// )
//
// // ...
//
// func (m *MyModule) GenerateBuildActions(ctx blueprint.Module) {
//     ctx.Build(pctx, blueprint.BuildParams{
//         Rule:    SomeRule,
//         Outputs: []string{"$myPrivateVar"},
//     })
// }
type PackageContext interface {
 Import(pkgPath string)
 ImportAs(as, pkgPath string)

 StaticVariable(name, value string) Variable
 VariableFunc(name string, f func(ctx VariableFuncContext, config interface{}) (string, error)) Variable
 VariableConfigMethod(name string, method interface{}) Variable

 StaticPool(name string, params PoolParams) Pool
 PoolFunc(name string, f func(interface{}) (PoolParams, error)) Pool

 StaticRule(name string, params RuleParams, argNames ...string) Rule
 RuleFunc(name string, f func(interface{}) (RuleParams, error), argNames ...string) Rule

 HostToolFunc(f func(config interface{}) (HostToolParams, error)) HostTool

 AddNinjaFileDeps(deps ...string)

 getScope() *basicScope
}

type packageContext struct {
 fullName      string
 shortName     string
 pkgPath       string
 scope         *basicScope
 ninjaFileDeps []string
}

var _ PackageContext = (*packageContext)(nil)

func (p *packageContext) getScope() *basicScope {
 return p.scope
}

var packageContexts = map[string]*packageContext{}

// NewPackageContext creates a PackageContext object for a given package.  The
// pkgPath argument should always be set to the full path used to import the
// package.  This function may only be called from a Go package's init()
// function or as part of a package-scoped variable initialization.
func NewPackageContext(pkgPath string) PackageContext {
 checkCalledFromInit()

 if _, present := packageContexts[pkgPath]; present {
  panic(fmt.Errorf("package %q already has a package context", pkgPath))
 }

 pkgName := pkgPathToName(pkgPath)
 err := validateNinjaName(pkgName)
 if err != nil {
  panic(err)
 }

 i := strings.LastIndex(pkgPath, "/")
 shortName := pkgPath[i+1:]

 p := &packageContext{
  fullName:  pkgName,
  shortName: shortName,
  pkgPath:   pkgPath,
  scope:     newScope(nil),
 }

 packageContexts[pkgPath] = p

 return p
}

var Phony Rule = NewBuiltinRule("phony")

var Console Pool = NewBuiltinPool("console")

var errRuleIsBuiltin = errors.New("the rule is a built-in")
var errPoolIsBuiltin = errors.New("the pool is a built-in")
var errVariableIsArg = errors.New("argument variables have no value")

// checkCalledFromInit panics if a Go package's init function is not on the
// call stack.
func checkCalledFromInit() {
 for skip := 3; ; skip++ {
  _, funcName, ok := callerName(skip)
  if !ok {
   panic("not called from an init func")
  }

  if funcName == "init" || strings.HasPrefix(funcName, "init·") ||
   funcName == "init.ializers" || strings.HasPrefix(funcName, "init.") {
   return
  }
 }
}

// A regex to find a package path within a function name. It finds the shortest string that is
// followed by '.' and doesn't have any '/'s left.
var pkgPathRe = regexp.MustCompile(`^(.*?)\.([^/]+)$`)

// callerName returns the package path and function name of the calling
// function.  The skip argument has the same meaning as the skip argument of
// runtime.Callers.
func callerName(skip int) (pkgPath, funcName string, ok bool) {
 var pc [1]uintptr
 n := runtime.Callers(skip+1, pc[:])
 if n != 1 {
  return "", "", false
 }
 frames := runtime.CallersFrames(pc[:])
 frame, _ := frames.Next()
 f := frame.Function
 s := pkgPathRe.FindStringSubmatch(f)
 if len(s) < 3 {
  panic(fmt.Errorf("failed to extract package path and function name from %q", f))
 }

 return s[1], s[2], true
}

// pkgPathToName makes a Ninja-friendly name out of a Go package name by
// replaceing all the '/' characters with '.'.  We assume the results are
// unique, though this is not 100% guaranteed for Go package names that
// already contain '.' characters. Disallowing package names with '.' isn't
// reasonable since many package names contain the name of the hosting site
// (e.g. "code.google.com").  In practice this probably isn't really a
// problem.
func pkgPathToName(pkgPath string) string {
 return strings.Replace(pkgPath, "/", ".", -1)
}

// Import enables access to the exported Ninja pools, rules, and variables
// that are defined at the package scope of another Go package.  Go's
// visibility rules apply to these references - capitalized names indicate
// that something is exported.  It may only be called from a Go package's
// init() function.  The Go package path passed to Import must have already
// been imported into the Go package using a Go import statement.  The
// imported variables may then be accessed from Ninja strings as
// "${pkg.Variable}", while the imported rules can simply be accessed as
// exported Go variables from the package.  For example:
//
// import (
//     "blueprint"
//     "foo/bar"
// )
//
// var pctx = NewPackagePath("blah")
//
// func init() {
//     pctx.Import("foo/bar")
// }
//
// ...
//
// func (m *MyModule) GenerateBuildActions(ctx blueprint.Module) {
//     ctx.Build(pctx, blueprint.BuildParams{
//         Rule:    bar.SomeRule,
//         Outputs: []string{"${bar.SomeVariable}"},
//     })
// }
//
// Note that the local name used to refer to the package in Ninja variable names
// is derived from pkgPath by extracting the last path component.  This differs
// from Go's import declaration, which derives the local name from the package
// clause in the imported package.  By convention these names are made to match,
// but this is not required.
func (p *packageContext) Import(pkgPath string) {
 checkCalledFromInit()
 importPkg, ok := packageContexts[pkgPath]
 if !ok {
  panic(fmt.Errorf("package %q has no context", pkgPath))
 }

 err := p.scope.AddImport(importPkg.shortName, importPkg.scope)
 if err != nil {
  panic(err)
 }
}

// ImportAs provides the same functionality as Import, but it allows the local
// name that will be used to refer to the package to be specified explicitly.
// It may only be called from a Go package's init() function.
func (p *packageContext) ImportAs(as, pkgPath string) {
 checkCalledFromInit()
 importPkg, ok := packageContexts[pkgPath]
 if !ok {
  panic(fmt.Errorf("package %q has no context", pkgPath))
 }

 err := validateNinjaName(as)
 if err != nil {
  panic(err)
 }

 err = p.scope.AddImport(as, importPkg.scope)
 if err != nil {
  panic(err)
 }
}

type staticVariable struct {
 pctx   *packageContext
 name_  string
 value_ string
}

// StaticVariable returns a Variable whose value does not depend on any
// configuration information.  It may only be called during a Go package's
// initialization - either from the init() function or as part of a package-
// scoped variable's initialization.
//
// This function is usually used to initialize a package-scoped Go variable that
// represents a Ninja variable that will be output.  The name argument should
// exactly match the Go variable name, and the value string may reference other
// Ninja variables that are visible within the calling Go package.
func (p *packageContext) StaticVariable(name, value string) Variable {
 checkCalledFromInit()
 err := validateNinjaName(name)
 if err != nil {
  panic(err)
 }

 v := &staticVariable{
  pctx:   p,
  name_:  name,
  value_: value,
 }
 err = p.scope.AddVariable(v)
 if err != nil {
  panic(err)
 }

 return v
}

func (v *staticVariable) packageContext() *packageContext {
 return v.pctx
}

func (v *staticVariable) name() string {
 return v.name_
}

func (v *staticVariable) fullName(pkgNames map[*packageContext]string) string {
 return packageNamespacePrefix(pkgNames[v.pctx]) + v.name_
}

func (v *staticVariable) value(VariableFuncContext, interface{}) (*ninjaString, error) {
 ninjaStr, err := parseNinjaString(v.pctx.scope, v.value_)
 if err != nil {
  err = fmt.Errorf("error parsing variable %s value: %s", v, err)
  panic(err)
 }
 return ninjaStr, nil
}

func (v *staticVariable) String() string {
 return v.pctx.pkgPath + "." + v.name_
}

type variableFunc struct {
 pctx   *packageContext
 name_  string
 value_ func(VariableFuncContext, interface{}) (string, error)
}

// VariableFuncContext is passed to VariableFunc functions.
type VariableFuncContext interface {
 // GlobWithDeps returns a list of files and directories that match the
 // specified pattern but do not match any of the patterns in excludes.
 // Any directories will have a '/' suffix.  It also adds efficient
 // dependencies to rerun the primary builder whenever a file matching
 // the pattern as added or removed, without rerunning if a file that
 // does not match the pattern is added to a searched directory.
 GlobWithDeps(globPattern string, excludes []string) ([]string, error)
}

type variableFuncContext struct {
 context *Context
}

func (v *variableFuncContext) GlobWithDeps(pattern string,
 excludes []string) ([]string, error) {
 return v.context.glob(pattern, excludes)
}

// VariableFunc returns a Variable whose value is determined by a function that
// takes a config object as input and returns either the variable value or an
// error.  It may only be called during a Go package's initialization - either
// from the init() function or as part of a package-scoped variable's
// initialization.
//
// This function is usually used to initialize a package-scoped Go variable that
// represents a Ninja variable that will be output.  The name argument should
// exactly match the Go variable name, and the value string returned by f may
// reference other Ninja variables that are visible within the calling Go
// package.
func (p *packageContext) VariableFunc(name string,
 f func(ctx VariableFuncContext, config interface{}) (string, error)) Variable {

 checkCalledFromInit()

 err := validateNinjaName(name)
 if err != nil {
  panic(err)
 }

 v := &variableFunc{
  pctx:   p,
  name_:  name,
  value_: f,
 }
 err = p.scope.AddVariable(v)
 if err != nil {
  panic(err)
 }

 return v
}

// VariableConfigMethod returns a Variable whose value is determined by calling
// a method on the config object.  The method must take no arguments and return
// a single string that will be the variable's value.  It may only be called
// during a Go package's initialization - either from the init() function or as
// part of a package-scoped variable's initialization.
//
// This function is usually used to initialize a package-scoped Go variable that
// represents a Ninja variable that will be output.  The name argument should
// exactly match the Go variable name, and the value string returned by method
// may reference other Ninja variables that are visible within the calling Go
// package.
func (p *packageContext) VariableConfigMethod(name string,
 method interface{}) Variable {

 checkCalledFromInit()

 err := validateNinjaName(name)
 if err != nil {
  panic(err)
 }

 methodValue := reflect.ValueOf(method)
 validateVariableMethod(name, methodValue)

 fun := func(ctx VariableFuncContext, config interface{}) (string, error) {
  result := methodValue.Call([]reflect.Value{reflect.ValueOf(config)})
  resultStr := result[0].Interface().(string)
  return resultStr, nil
 }

 v := &variableFunc{
  pctx:   p,
  name_:  name,
  value_: fun,
 }
 err = p.scope.AddVariable(v)
 if err != nil {
  panic(err)
 }

 return v
}

func (v *variableFunc) packageContext() *packageContext {
 return v.pctx
}

func (v *variableFunc) name() string {
 return v.name_
}

func (v *variableFunc) fullName(pkgNames map[*packageContext]string) string {
 return packageNamespacePrefix(pkgNames[v.pctx]) + v.name_
}

func (v *variableFunc) value(ctx VariableFuncContext, config interface{}) (*ninjaString, error) {
 value, err := v.value_(ctx, config)
 if err != nil {
  return nil, err
 }

 ninjaStr, err := parseNinjaString(v.pctx.scope, value)
 if err != nil {
  err = fmt.Errorf("error parsing variable %s value: %s", v, err)
  panic(err)
 }

 return ninjaStr, nil
}

func (v *variableFunc) String() string {
 return v.pctx.pkgPath + "." + v.name_
}

func validateVariableMethod(name string, methodValue reflect.Value) {
 methodType := methodValue.Type()
 if methodType.Kind() != reflect.Func {
  panic(fmt.Errorf("method given for variable %s is not a function",
   name))
 }
 if n := methodType.NumIn(); n != 1 {
  panic(fmt.Errorf("method for variable %s has %d inputs (should be 1)",
   name, n))
 }
 if n := methodType.NumOut(); n != 1 {
  panic(fmt.Errorf("method for variable %s has %d outputs (should be 1)",
   name, n))
 }
 if kind := methodType.Out(0).Kind(); kind != reflect.String {
  panic(fmt.Errorf("method for variable %s does not return a string",
   name))
 }
}

// An argVariable is a Variable that exists only when it is set by a build
// statement to pass a value to the rule being invoked.  It has no value, so it
// can never be used to create a Ninja assignment statement.  It is inserted
// into the rule's scope, which is used for name lookups within the rule and
// when assigning argument values as part of a build statement.
type argVariable struct {
 name_ string
}

func (v *argVariable) packageContext() *packageContext {
 panic("this should not be called")
}

func (v *argVariable) name() string {
 return v.name_
}

func (v *argVariable) fullName(pkgNames map[*packageContext]string) string {
 return v.name_
}

func (v *argVariable) value(ctx VariableFuncContext, config interface{}) (*ninjaString, error) {
 return nil, errVariableIsArg
}

func (v *argVariable) String() string {
 return "<arg>:" + v.name_
}

type staticPool struct {
 pctx   *packageContext
 name_  string
 params PoolParams
}

// StaticPool returns a Pool whose value does not depend on any configuration
// information.  It may only be called during a Go package's initialization -
// either from the init() function or as part of a package-scoped Go variable's
// initialization.
//
// This function is usually used to initialize a package-scoped Go variable that
// represents a Ninja pool that will be output.  The name argument should
// exactly match the Go variable name, and the params fields may reference other
// Ninja variables that are visible within the calling Go package.
func (p *packageContext) StaticPool(name string, params PoolParams) Pool {
 checkCalledFromInit()

 err := validateNinjaName(name)
 if err != nil {
  panic(err)
 }

 pool := &staticPool{
  pctx:   p,
  name_:  name,
  params: params,
 }
 err = p.scope.AddPool(pool)
 if err != nil {
  panic(err)
 }

 return pool
}

func (p *staticPool) packageContext() *packageContext {
 return p.pctx
}

func (p *staticPool) name() string {
 return p.name_
}

func (p *staticPool) fullName(pkgNames map[*packageContext]string) string {
 return packageNamespacePrefix(pkgNames[p.pctx]) + p.name_
}

func (p *staticPool) def(config interface{}) (*poolDef, error) {
 def, err := parsePoolParams(p.pctx.scope, &p.params)
 if err != nil {
  panic(fmt.Errorf("error parsing PoolParams for %s: %s", p, err))
 }
 return def, nil
}

func (p *staticPool) String() string {
 return p.pctx.pkgPath + "." + p.name_
}

type poolFunc struct {
 pctx       *packageContext
 name_      string
 paramsFunc func(interface{}) (PoolParams, error)
}

// PoolFunc returns a Pool whose value is determined by a function that takes a
// config object as input and returns either the pool parameters or an error. It
// may only be called during a Go package's initialization - either from the
// init() function or as part of a package-scoped variable's initialization.
//
// This function is usually used to initialize a package-scoped Go variable that
// represents a Ninja pool that will be output.  The name argument should
// exactly match the Go variable name, and the string fields of the PoolParams
// returned by f may reference other Ninja variables that are visible within the
// calling Go package.
func (p *packageContext) PoolFunc(name string, f func(interface{}) (PoolParams,
 error)) Pool {

 checkCalledFromInit()

 err := validateNinjaName(name)
 if err != nil {
  panic(err)
 }

 pool := &poolFunc{
  pctx:       p,
  name_:      name,
  paramsFunc: f,
 }
 err = p.scope.AddPool(pool)
 if err != nil {
  panic(err)
 }

 return pool
}

func (p *poolFunc) packageContext() *packageContext {
 return p.pctx
}

func (p *poolFunc) name() string {
 return p.name_
}

func (p *poolFunc) fullName(pkgNames map[*packageContext]string) string {
 return packageNamespacePrefix(pkgNames[p.pctx]) + p.name_
}

func (p *poolFunc) def(config interface{}) (*poolDef, error) {
 params, err := p.paramsFunc(config)
 if err != nil {
  return nil, err
 }
 def, err := parsePoolParams(p.pctx.scope, ¶ms)
 if err != nil {
  panic(fmt.Errorf("error parsing PoolParams for %s: %s", p, err))
 }
 return def, nil
}

func (p *poolFunc) String() string {
 return p.pctx.pkgPath + "." + p.name_
}

type builtinPool struct {
 name_ string
}

func (p *builtinPool) packageContext() *packageContext {
 return nil
}

func (p *builtinPool) name() string {
 return p.name_
}

func (p *builtinPool) fullName(pkgNames map[*packageContext]string) string {
 return p.name_
}

func (p *builtinPool) def(config interface{}) (*poolDef, error) {
 return nil, errPoolIsBuiltin
}

// NewBuiltinPool returns a Pool object that refers to a pool name created outside of Blueprint
func NewBuiltinPool(name string) Pool {
 return &builtinPool{
  name_: name,
 }
}

func (p *builtinPool) String() string {
 return "<builtin>:" + p.name_
}

// StaticRule returns a Rule whose value does not depend on any configuration
// information.  It may only be called during a Go package's initialization -
// either from the init() function or as part of a package-scoped Go variable's
// initialization.
//
// This function is usually used to initialize a package-scoped Go variable that
// represents a Ninja rule that will be output.  The name argument should
// exactly match the Go variable name, and the params fields may reference other
// Ninja variables that are visible within the calling Go package.
//
// The argNames arguments list Ninja variables that may be overridden by Ninja
// build statements that invoke the rule.  These arguments may be referenced in
// any of the string fields of params.  Arguments can shadow package-scoped
// variables defined within the caller's Go package, but they may not shadow
// those defined in another package.  Shadowing a package-scoped variable
// results in the package-scoped variable's value being used for build
// statements that do not override the argument.  For argument names that do not
// shadow package-scoped variables the default value is an empty string.
func (p *packageContext) StaticRule(name string, params RuleParams,
 argNames ...string) Rule {

 checkCalledFromInit()

 return p.RuleFunc(name, func(config interface{}) (RuleParams, error) {
  return params, nil
 }, argNames...)
}

type ruleFunc struct {
 pctx       *packageContext
 name_      string
 paramsFunc func(interface{}) (RuleParams, error)
 argNames   map[string]bool
 scope_     *basicScope
 sync.Mutex // protects scope_ during lazy creation
}

// RuleFunc returns a Rule whose value is determined by a function that takes a
// config object as input and returns either the rule parameters or an error. It
// may only be called during a Go package's initialization - either from the
// init() function or as part of a package-scoped variable's initialization.
//
// This function is usually used to initialize a package-scoped Go variable that
// represents a Ninja rule that will be output.  The name argument should
// exactly match the Go variable name, and the string fields of the RuleParams
// returned by f may reference other Ninja variables that are visible within the
// calling Go package.
//
// The argNames arguments list Ninja variables that may be overridden by Ninja
// build statements that invoke the rule.  These arguments may be referenced in
// any of the string fields of the RuleParams returned by f.  Arguments can
// shadow package-scoped variables defined within the caller's Go package, but
// they may not shadow those defined in another package.  Shadowing a package-
// scoped variable results in the package-scoped variable's value being used for
// build statements that do not override the argument.  For argument names that
// do not shadow package-scoped variables the default value is an empty string.
func (p *packageContext) RuleFunc(name string, f func(interface{}) (RuleParams,
 error), argNames ...string) Rule {

 checkCalledFromInit()

 err := validateNinjaName(name)
 if err != nil {
  panic(err)
 }

 err = validateArgNames(argNames)
 if err != nil {
  panic(fmt.Errorf("invalid argument name: %s", err))
 }

 argNamesSet := make(map[string]bool)
 for _, argName := range argNames {
  argNamesSet[argName] = true
 }

 ruleScope := (*basicScope)(nil) // This will get created lazily

 rule := &ruleFunc{
  pctx:       p,
  name_:      name,
  paramsFunc: f,
  argNames:   argNamesSet,
  scope_:     ruleScope,
 }
 err = p.scope.AddRule(rule)
 if err != nil {
  panic(err)
 }

 return rule
}

func (r *ruleFunc) packageContext() *packageContext {
 return r.pctx
}

func (r *ruleFunc) name() string {
 return r.name_
}

func (r *ruleFunc) fullName(pkgNames map[*packageContext]string) string {
 return packageNamespacePrefix(pkgNames[r.pctx]) + r.name_
}

func (r *ruleFunc) def(config interface{}) (*ruleDef, error) {
 params, err := r.paramsFunc(config)
 if err != nil {
  return nil, err
 }

 def, err := parseRuleParams(config, r.scope(), ¶ms)
 if err != nil {
  panic(fmt.Errorf("error parsing RuleParams for %s: %s", r, err))
 }
 return def, nil
}

func (r *ruleFunc) scope() *basicScope {
 // We lazily create the scope so that all the global variables get declared
 // before the args are created.  Otherwise we could incorrectly shadow a
 // global variable with an arg variable.
 r.Lock()
 defer r.Unlock()

 if r.scope_ == nil {
  r.scope_ = makeRuleScope(r.pctx.scope, r.argNames)
 }
 return r.scope_
}

func (r *ruleFunc) isArg(argName string) bool {
 return r.argNames[argName]
}

func (r *ruleFunc) String() string {
 return r.pctx.pkgPath + "." + r.name_
}

type builtinRule struct {
 name_      string
 scope_     *basicScope
 sync.Mutex // protects scope_ during lazy creation
}

func (r *builtinRule) packageContext() *packageContext {
 return nil
}

func (r *builtinRule) name() string {
 return r.name_
}

func (r *builtinRule) fullName(pkgNames map[*packageContext]string) string {
 return r.name_
}

func (r *builtinRule) def(config interface{}) (*ruleDef, error) {
 return nil, errRuleIsBuiltin
}

func (r *builtinRule) scope() *basicScope {
 r.Lock()
 defer r.Unlock()

 if r.scope_ == nil {
  r.scope_ = makeRuleScope(nil, nil)
 }
 return r.scope_
}

func (r *builtinRule) isArg(argName string) bool {
 return false
}

func (r *builtinRule) String() string {
 return "<builtin>:" + r.name_
}

// NewBuiltinRule returns a Rule object that refers to a rule that was created outside of Blueprint
func NewBuiltinRule(name string) Rule {
 return &builtinRule{
  name_: name,
 }
}

func (p *packageContext) HostToolFunc(f func(config interface{}) (HostToolParams, error)) HostTool {
 checkCalledFromInit()
 return HostTool{
  inner: &hostToolInner{
   f: f,
  },
 }
}

func (p *packageContext) AddNinjaFileDeps(deps ...string) {
 p.ninjaFileDeps = append(p.ninjaFileDeps, deps...)
}

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