github.phpd.cn/thought-machine/please@v12.2.0+incompatible/src/parse/asp/builtins.go (about) 1 package asp 2 3 import ( 4 "fmt" 5 "path" 6 "reflect" 7 "sort" 8 "strconv" 9 "strings" 10 11 "core" 12 "fs" 13 ) 14 15 // A few sneaky globals for when we don't have a scope handy 16 var stringMethods, dictMethods, configMethods map[string]*pyFunc 17 18 const subincludePackageName = "_remote" 19 20 // A nativeFunc is a function that implements a builtin function natively. 21 type nativeFunc func(*scope, []pyObject) pyObject 22 23 // registerBuiltins sets up the "special" builtins that map to native code. 24 func registerBuiltins(s *scope) { 25 setNativeCode(s, "build_rule", buildRule) 26 setNativeCode(s, "subrepo", subrepo) 27 setNativeCode(s, "fail", builtinFail) 28 setNativeCode(s, "subinclude", subinclude) 29 setNativeCode(s, "load", bazelLoad).varargs = true 30 setNativeCode(s, "package", pkg).kwargs = true 31 setNativeCode(s, "sorted", sorted) 32 setNativeCode(s, "isinstance", isinstance) 33 setNativeCode(s, "range", pyRange) 34 setNativeCode(s, "enumerate", enumerate) 35 setNativeCode(s, "zip", zip).varargs = true 36 setNativeCode(s, "len", lenFunc) 37 setNativeCode(s, "glob", glob) 38 setNativeCode(s, "bool", boolType) 39 setNativeCode(s, "int", intType) 40 setNativeCode(s, "str", strType) 41 setNativeCode(s, "join_path", joinPath).varargs = true 42 setNativeCode(s, "get_base_path", getBasePath) 43 setNativeCode(s, "package_name", packageName) 44 setNativeCode(s, "get_labels", getLabels) 45 setNativeCode(s, "add_dep", addDep) 46 setNativeCode(s, "add_out", addOut) 47 setNativeCode(s, "add_licence", addLicence) 48 setNativeCode(s, "get_command", getCommand) 49 setNativeCode(s, "set_command", setCommand) 50 stringMethods = map[string]*pyFunc{ 51 "join": setNativeCode(s, "join", strJoin), 52 "split": setNativeCode(s, "split", strSplit), 53 "replace": setNativeCode(s, "replace", strReplace), 54 "partition": setNativeCode(s, "partition", strPartition), 55 "rpartition": setNativeCode(s, "rpartition", strRPartition), 56 "startswith": setNativeCode(s, "startswith", strStartsWith), 57 "endswith": setNativeCode(s, "endswith", strEndsWith), 58 "lstrip": setNativeCode(s, "lstrip", strLStrip), 59 "rstrip": setNativeCode(s, "rstrip", strRStrip), 60 "strip": setNativeCode(s, "strip", strStrip), 61 "find": setNativeCode(s, "find", strFind), 62 "rfind": setNativeCode(s, "find", strRFind), 63 "format": setNativeCode(s, "format", strFormat), 64 "count": setNativeCode(s, "count", strCount), 65 "upper": setNativeCode(s, "upper", strUpper), 66 "lower": setNativeCode(s, "lower", strLower), 67 } 68 stringMethods["format"].kwargs = true 69 dictMethods = map[string]*pyFunc{ 70 "get": setNativeCode(s, "get", dictGet), 71 "setdefault": s.Lookup("setdefault").(*pyFunc), 72 "keys": setNativeCode(s, "keys", dictKeys), 73 "items": setNativeCode(s, "items", dictItems), 74 "values": setNativeCode(s, "values", dictValues), 75 "copy": setNativeCode(s, "copy", dictCopy), 76 } 77 configMethods = map[string]*pyFunc{ 78 "get": setNativeCode(s, "config_get", configGet), 79 "setdefault": s.Lookup("setdefault").(*pyFunc), 80 } 81 setLogCode(s, "debug", log.Debug) 82 setLogCode(s, "info", log.Info) 83 setLogCode(s, "notice", log.Notice) 84 setLogCode(s, "warning", log.Warning) 85 setLogCode(s, "error", log.Errorf) 86 setLogCode(s, "fatal", log.Fatalf) 87 } 88 89 // registerSubincludePackage sets up the package for remote subincludes. 90 func registerSubincludePackage(s *scope) { 91 var pkg *core.Package 92 if pkg = s.state.Graph.Package(subincludePackageName); pkg == nil { 93 pkg = core.NewPackage(subincludePackageName) 94 s.state.Graph.AddPackage(pkg) 95 } 96 s.interpreter.subincludeScope = s.NewPackagedScope(pkg) 97 // Always counts as being in callback mode (i.e. the package is already parsed and we are adding individual targets later). 98 s.interpreter.subincludeScope.Callback = true 99 // Another small hack - replace the code for these two with native code, must be done after the 100 // declarations which are in misc_rules. 101 buildRule := s.Lookup("build_rule").(*pyFunc) 102 f := setNativeCode(s, "filegroup", filegroup) 103 f.args = buildRule.args 104 f.argIndices = buildRule.argIndices 105 f.defaults = buildRule.defaults 106 f.constants = buildRule.constants 107 f.types = buildRule.types 108 f = setNativeCode(s, "hash_filegroup", hashFilegroup) 109 f.args = buildRule.args 110 f.argIndices = buildRule.argIndices 111 f.defaults = buildRule.defaults 112 f.constants = buildRule.constants 113 f.types = buildRule.types 114 } 115 116 func setNativeCode(s *scope, name string, code nativeFunc) *pyFunc { 117 f := s.Lookup(name).(*pyFunc) 118 f.nativeCode = code 119 f.code = nil // Might as well save a little memory here 120 return f 121 } 122 123 // setLogCode specialises setNativeCode for handling the log functions (of which there are a few) 124 func setLogCode(s *scope, name string, f func(format string, args ...interface{})) { 125 setNativeCode(s, name, func(s *scope, args []pyObject) pyObject { 126 if str, ok := args[0].(pyString); ok { 127 l := make([]interface{}, len(args)) 128 for i, arg := range args { 129 l[i] = arg 130 } 131 f("//%s: %s", s.pkgFilename(), fmt.Sprintf(string(str), l[1:]...)) 132 return None 133 } 134 f("//%s: %s", s.pkgFilename(), args) 135 return None 136 }).varargs = true 137 } 138 139 // buildRule implements the build_rule() builtin function. 140 // This is the main interface point; every build rule ultimately calls this to add 141 // new objects to the build graph. 142 func buildRule(s *scope, args []pyObject) pyObject { 143 s.NAssert(s.pkg == nil, "Cannot create new build rules in this context") 144 // We need to set various defaults from config here; it is useful to put it on the rule but not often so 145 // because most rules pass them through anyway. 146 // TODO(peterebden): when we get rid of the old parser, put these defaults on all the build rules and 147 // get rid of this. 148 config := s.Lookup("CONFIG").(*pyConfig) 149 args[11] = defaultFromConfig(config, args[11], "DEFAULT_VISIBILITY") 150 args[15] = defaultFromConfig(config, args[15], "DEFAULT_TESTONLY") 151 args[30] = defaultFromConfig(config, args[30], "DEFAULT_LICENCES") 152 args[20] = defaultFromConfig(config, args[20], "BUILD_SANDBOX") 153 args[21] = defaultFromConfig(config, args[21], "TEST_SANDBOX") 154 target := createTarget(s, args) 155 s.Assert(s.pkg.Target(target.Label.Name) == nil, "Duplicate build target in %s: %s", s.pkg.Name, target.Label.Name) 156 s.pkg.AddTarget(target) 157 populateTarget(s, target, args) 158 if s.Callback { 159 // We are in a post-build function, so add the target directly to the graph now. 160 log.Debug("Adding new target %s directly to graph", target.Label) 161 target.AddedPostBuild = true 162 s.state.Graph.AddTarget(target) 163 s.pkg.MarkTargetModified(target) 164 } 165 return pyString(":" + target.Label.Name) 166 } 167 168 // filegroup implements the filegroup() builtin. 169 func filegroup(s *scope, args []pyObject) pyObject { 170 args[1] = filegroupCommand 171 return buildRule(s, args) 172 } 173 174 // hashFilegroup implements the hash_filegroup() builtin. 175 func hashFilegroup(s *scope, args []pyObject) pyObject { 176 args[1] = hashFilegroupCommand 177 return buildRule(s, args) 178 } 179 180 // defaultFromConfig sets a default value from the config if the property isn't set. 181 func defaultFromConfig(config *pyConfig, arg pyObject, name string) pyObject { 182 if arg == nil || arg == None { 183 return config.Get(name, arg) 184 } 185 return arg 186 } 187 188 // pkg implements the package() builtin function. 189 func pkg(s *scope, args []pyObject) pyObject { 190 s.Assert(s.pkg.NumTargets() == 0, "package() must be called before any build targets are defined") 191 c, ok := s.Lookup("CONFIG").(*pyConfig) 192 s.Assert(ok, "CONFIG object has been altered") 193 for k, v := range s.locals { 194 k = strings.ToUpper(k) 195 s.Assert(c.Get(k, nil) != nil, "error calling package(): %s is not a known config value", k) 196 c.IndexAssign(pyString(k), v) 197 } 198 return None 199 } 200 201 // tagName applies the given tag to a target name. 202 func tagName(name, tag string) string { 203 if name[0] != '_' { 204 name = "_" + name 205 } 206 if strings.ContainsRune(name, '#') { 207 name = name + "_" 208 } else { 209 name = name + "#" 210 } 211 return name + tag 212 } 213 214 // bazelLoad implements the load() builtin, which is only available for Bazel compatibility. 215 func bazelLoad(s *scope, args []pyObject) pyObject { 216 s.Assert(s.state.Config.Bazel.Compatibility, "load() is only available in Bazel compatibility mode. See `plz help bazel` for more information.") 217 // The argument always looks like a build label, but it is not really one (i.e. there is no BUILD file that defines it). 218 // We do not support their legacy syntax here (i.e. "/tools/build_rules/build_test" etc). 219 l := core.ParseBuildLabel(string(args[0].(pyString)), s.pkg.Name) 220 s.SetAll(s.interpreter.Subinclude(path.Join(l.PackageName, l.Name)), false) 221 return None 222 } 223 224 // builtinFail raises an immediate error that can't be intercepted. 225 func builtinFail(s *scope, args []pyObject) pyObject { 226 s.Error(string(args[0].(pyString))) 227 return None 228 } 229 230 func subinclude(s *scope, args []pyObject) pyObject { 231 t := subincludeTarget(s, subincludeLabel(s, args)) 232 for _, out := range t.Outputs() { 233 s.SetAll(s.interpreter.Subinclude(path.Join(t.OutDir(), out)), false) 234 } 235 return None 236 } 237 238 // subincludeTarget returns the target for a subinclude() call to a label. 239 // It blocks until the target exists and is built. 240 func subincludeTarget(s *scope, l core.BuildLabel) *core.BuildTarget { 241 if s.pkg == nil { 242 // Really we should not get here, but it's hard to prove that's not the case. Make the best of it. 243 return s.state.WaitForBuiltTarget(l, l.PackageName) 244 } 245 t := s.state.WaitForBuiltTarget(l, s.pkg.Name) 246 if l.PackageName != subincludePackageName { 247 s.pkg.RegisterSubinclude(l) 248 } 249 return t 250 } 251 252 // subincludeLabel returns the label for a subinclude() call (which might be indirect 253 // if the given argument was a URL instead of a build label) 254 func subincludeLabel(s *scope, args []pyObject) core.BuildLabel { 255 target := string(args[0].(pyString)) 256 s.NAssert(strings.HasPrefix(target, ":"), "Subincludes cannot be from the local package") 257 if !strings.HasPrefix(target, "http") { 258 return core.ParseBuildLabel(target, "") 259 } 260 // Check if this target is already registered (this will always happen eventually because 261 // we re-parse the same package again). 262 name := strings.Replace(path.Base(target), ".", "_", -1) 263 label := core.NewBuildLabel(subincludePackageName, name) 264 if s.state.Graph.Target(label) != nil { 265 return label 266 } 267 remoteFile, ok := s.interpreter.subincludeScope.Lookup("remote_file").(*pyFunc) 268 s.interpreter.subincludeScope.Assert(ok, "remote_file is not callable") 269 // Call using the normal entry point, which is a bit of a faff but it sorts out default arguments and so forth 270 a := []CallArgument{ 271 { 272 Name: "name", 273 Value: Expression{Val: &ValueExpression{String: `"` + name + `"`}}, 274 }, { 275 Name: "url", 276 Value: Expression{Val: &ValueExpression{String: `"` + target + `"`}}, 277 }, 278 } 279 if args[1] != nil && args[1] != None { 280 a = append(a, CallArgument{ 281 Name: "hashes", 282 Value: Expression{Val: &ValueExpression{List: &List{ 283 Values: []*Expression{{Val: &ValueExpression{ 284 String: `"` + string(args[1].(pyString)) + `"`, 285 }}}, 286 }}}, 287 }) 288 } 289 remoteFile.Call(s.interpreter.subincludeScope, &Call{Arguments: a}) 290 return label 291 } 292 293 func lenFunc(s *scope, args []pyObject) pyObject { 294 return pyInt(args[0].Len()) 295 } 296 297 func isinstance(s *scope, args []pyObject) pyObject { 298 obj := args[0] 299 types := args[1] 300 if f, ok := types.(*pyFunc); ok && isType(obj, f.name) { 301 // Special case for 'str' and so forth that are functions but also types. 302 return True 303 } else if l, ok := types.(pyList); ok { 304 for _, li := range l { 305 if lif, ok := li.(*pyFunc); ok && isType(obj, lif.name) { 306 return True 307 } else if reflect.TypeOf(obj) == reflect.TypeOf(li) { 308 return True 309 } 310 } 311 } 312 return newPyBool(reflect.TypeOf(obj) == reflect.TypeOf(types)) 313 } 314 315 func isType(obj pyObject, name string) bool { 316 switch obj.(type) { 317 case pyBool: 318 return name == "bool" || name == "int" // N.B. For compatibility with old assert statements 319 case pyInt: 320 return name == "int" 321 case pyString: 322 return name == "str" 323 case pyList: 324 return name == "list" 325 case pyDict: 326 return name == "dict" 327 } 328 return false 329 } 330 331 func strJoin(s *scope, args []pyObject) pyObject { 332 self := string(args[0].(pyString)) 333 seq := asStringList(s, args[1], "seq") 334 return pyString(strings.Join(seq, self)) 335 } 336 337 func strSplit(s *scope, args []pyObject) pyObject { 338 self := args[0].(pyString) 339 on := args[1].(pyString) 340 return fromStringList(strings.Split(string(self), string(on))) 341 } 342 343 func strReplace(s *scope, args []pyObject) pyObject { 344 self := args[0].(pyString) 345 old := args[1].(pyString) 346 new := args[2].(pyString) 347 return pyString(strings.Replace(string(self), string(old), string(new), -1)) 348 } 349 350 func strPartition(s *scope, args []pyObject) pyObject { 351 self := args[0].(pyString) 352 sep := args[1].(pyString) 353 if idx := strings.Index(string(self), string(sep)); idx != -1 { 354 return pyList{self[:idx], self[idx : idx+1], self[idx+1:]} 355 } 356 return pyList{self, pyString(""), pyString("")} 357 } 358 359 func strRPartition(s *scope, args []pyObject) pyObject { 360 self := args[0].(pyString) 361 sep := args[1].(pyString) 362 if idx := strings.LastIndex(string(self), string(sep)); idx != -1 { 363 return pyList{self[:idx], self[idx : idx+1], self[idx+1:]} 364 } 365 return pyList{pyString(""), pyString(""), self} 366 } 367 368 func strStartsWith(s *scope, args []pyObject) pyObject { 369 self := args[0].(pyString) 370 x := args[1].(pyString) 371 return newPyBool(strings.HasPrefix(string(self), string(x))) 372 } 373 374 func strEndsWith(s *scope, args []pyObject) pyObject { 375 self := args[0].(pyString) 376 x := args[1].(pyString) 377 return newPyBool(strings.HasSuffix(string(self), string(x))) 378 } 379 380 func strLStrip(s *scope, args []pyObject) pyObject { 381 self := args[0].(pyString) 382 cutset := args[1].(pyString) 383 return pyString(strings.TrimLeft(string(self), string(cutset))) 384 } 385 386 func strRStrip(s *scope, args []pyObject) pyObject { 387 self := args[0].(pyString) 388 cutset := args[1].(pyString) 389 return pyString(strings.TrimRight(string(self), string(cutset))) 390 } 391 392 func strStrip(s *scope, args []pyObject) pyObject { 393 self := args[0].(pyString) 394 cutset := args[1].(pyString) 395 return pyString(strings.Trim(string(self), string(cutset))) 396 } 397 398 func strFind(s *scope, args []pyObject) pyObject { 399 self := args[0].(pyString) 400 needle := args[1].(pyString) 401 return pyInt(strings.Index(string(self), string(needle))) 402 } 403 404 func strRFind(s *scope, args []pyObject) pyObject { 405 self := args[0].(pyString) 406 needle := args[1].(pyString) 407 return pyInt(strings.LastIndex(string(self), string(needle))) 408 } 409 410 func strFormat(s *scope, args []pyObject) pyObject { 411 self := string(args[0].(pyString)) 412 for k, v := range s.locals { 413 self = strings.Replace(self, "{"+k+"}", v.String(), -1) 414 } 415 return pyString(strings.Replace(strings.Replace(self, "{{", "{", -1), "}}", "}", -1)) 416 } 417 418 func strCount(s *scope, args []pyObject) pyObject { 419 self := string(args[0].(pyString)) 420 needle := string(args[1].(pyString)) 421 return pyInt(strings.Count(self, needle)) 422 } 423 424 func strUpper(s *scope, args []pyObject) pyObject { 425 self := string(args[0].(pyString)) 426 return pyString(strings.ToUpper(self)) 427 } 428 429 func strLower(s *scope, args []pyObject) pyObject { 430 self := string(args[0].(pyString)) 431 return pyString(strings.ToLower(self)) 432 } 433 434 func boolType(s *scope, args []pyObject) pyObject { 435 return newPyBool(args[0].IsTruthy()) 436 } 437 438 func intType(s *scope, args []pyObject) pyObject { 439 i, err := strconv.Atoi(string(args[0].(pyString))) 440 s.Assert(err == nil, "%s", err) 441 return pyInt(i) 442 } 443 444 func strType(s *scope, args []pyObject) pyObject { 445 return pyString(args[0].String()) 446 } 447 448 func glob(s *scope, args []pyObject) pyObject { 449 include := asStringList(s, args[0], "include") 450 exclude := asStringList(s, args[1], "exclude") 451 hidden := args[2].IsTruthy() 452 exclude = append(exclude, s.state.Config.Parse.BuildFileName...) 453 return fromStringList(fs.Glob(s.state.Config.Parse.BuildFileName, s.pkg.SourceRoot(), include, exclude, exclude, hidden)) 454 } 455 456 func asStringList(s *scope, arg pyObject, name string) []string { 457 l, ok := arg.(pyList) 458 s.Assert(ok, "argument %s must be a list", name) 459 sl := make([]string, len(l)) 460 for i, x := range l { 461 sx, ok := x.(pyString) 462 s.Assert(ok, "%s must be a list of strings", name) 463 sl[i] = string(sx) 464 } 465 return sl 466 } 467 468 func fromStringList(l []string) pyList { 469 ret := make(pyList, len(l)) 470 for i, s := range l { 471 ret[i] = pyString(s) 472 } 473 return ret 474 } 475 476 func configGet(s *scope, args []pyObject) pyObject { 477 self := args[0].(*pyConfig) 478 return self.Get(string(args[1].(pyString)), args[2]) 479 } 480 481 func dictGet(s *scope, args []pyObject) pyObject { 482 self := args[0].(pyDict) 483 sk, ok := args[1].(pyString) 484 s.Assert(ok, "dict keys must be strings, not %s", args[1].Type()) 485 if ret, present := self[string(sk)]; present { 486 return ret 487 } 488 return args[2] 489 } 490 491 func dictKeys(s *scope, args []pyObject) pyObject { 492 self := args[0].(pyDict) 493 ret := make(pyList, len(self)) 494 for i, k := range self.Keys() { 495 ret[i] = pyString(k) 496 } 497 return ret 498 } 499 500 func dictValues(s *scope, args []pyObject) pyObject { 501 self := args[0].(pyDict) 502 ret := make(pyList, len(self)) 503 for i, k := range self.Keys() { 504 ret[i] = self[k] 505 } 506 return ret 507 } 508 509 func dictItems(s *scope, args []pyObject) pyObject { 510 self := args[0].(pyDict) 511 ret := make(pyList, len(self)) 512 for i, k := range self.Keys() { 513 ret[i] = pyList{pyString(k), self[k]} 514 } 515 return ret 516 } 517 518 func dictCopy(s *scope, args []pyObject) pyObject { 519 self := args[0].(pyDict) 520 ret := make(pyDict, len(self)) 521 for k, v := range self { 522 ret[k] = v 523 } 524 return ret 525 } 526 527 func sorted(s *scope, args []pyObject) pyObject { 528 l, ok := args[0].(pyList) 529 s.Assert(ok, "unsortable type %s", args[0].Type()) 530 l = l[:] 531 sort.Slice(l, func(i, j int) bool { return l[i].Operator(LessThan, l[j]).IsTruthy() }) 532 return l 533 } 534 535 func joinPath(s *scope, args []pyObject) pyObject { 536 l := make([]string, len(args)) 537 for i, arg := range args { 538 l[i] = string(arg.(pyString)) 539 } 540 return pyString(path.Join(l...)) 541 } 542 543 func getBasePath(s *scope, args []pyObject) pyObject { 544 return pyString(s.pkg.Name) 545 } 546 547 func packageName(s *scope, args []pyObject) pyObject { 548 if s.pkg.Subrepo != nil { 549 return pyString(s.pkg.Subrepo.MakeRelativeName(s.pkg.Name)) 550 } 551 return pyString(s.pkg.Name) 552 } 553 554 func pyRange(s *scope, args []pyObject) pyObject { 555 start := args[0].(pyInt) 556 stop, isInt := args[1].(pyInt) 557 step := args[2].(pyInt) 558 if !isInt { 559 // Stop not passed so we start at 0 and start is the stop. 560 stop = start 561 start = 0 562 } 563 ret := make(pyList, 0, stop-start) 564 for i := start; i < stop; i += step { 565 ret = append(ret, i) 566 } 567 return ret 568 } 569 570 func enumerate(s *scope, args []pyObject) pyObject { 571 l, ok := args[0].(pyList) 572 s.Assert(ok, "Argument to enumerate must be a list, not %s", args[0].Type()) 573 ret := make(pyList, len(l)) 574 for i, li := range l { 575 ret[i] = pyList{pyInt(i), li} 576 } 577 return ret 578 } 579 580 func zip(s *scope, args []pyObject) pyObject { 581 lastLen := 0 582 for i, seq := range args { 583 si, ok := seq.(pyList) 584 s.Assert(ok, "Arguments to zip must be lists, not %s", si.Type()) 585 // This isn't a restriction in Python but I can't be bothered handling all the stuff that real zip does. 586 s.Assert(i == 0 || lastLen == len(si), "All arguments to zip must have the same length") 587 lastLen = len(si) 588 } 589 ret := make(pyList, lastLen) 590 for i := range ret { 591 r := make(pyList, len(args)) 592 for j, li := range args { 593 r[j] = li.(pyList)[i] 594 } 595 ret[i] = r 596 } 597 return ret 598 } 599 600 // getLabels returns the set of labels for a build target and its transitive dependencies. 601 // The labels are filtered by the given prefix, which is stripped from the returned labels. 602 // Two formats are supported here: either passing just the name of a target in the current 603 // package, or a build label referring specifically to one. 604 func getLabels(s *scope, args []pyObject) pyObject { 605 name := string(args[0].(pyString)) 606 prefix := string(args[1].(pyString)) 607 if core.LooksLikeABuildLabel(name) { 608 label := core.ParseBuildLabel(name, s.pkg.Name) 609 return getLabelsInternal(s.state.Graph.TargetOrDie(label), prefix, core.Built) 610 } 611 target := getTargetPost(s, name) 612 return getLabelsInternal(target, prefix, core.Building) 613 } 614 615 func getLabelsInternal(target *core.BuildTarget, prefix string, minState core.BuildTargetState) pyObject { 616 if target.State() < minState { 617 log.Fatalf("get_labels called on a target that is not yet built: %s", target.Label) 618 } 619 labels := map[string]bool{} 620 done := map[*core.BuildTarget]bool{} 621 var getLabels func(*core.BuildTarget) 622 getLabels = func(t *core.BuildTarget) { 623 for _, label := range t.Labels { 624 if strings.HasPrefix(label, prefix) { 625 labels[strings.TrimSpace(strings.TrimPrefix(label, prefix))] = true 626 } 627 } 628 done[t] = true 629 if !t.OutputIsComplete || t == target { 630 for _, dep := range t.Dependencies() { 631 if !done[dep] { 632 getLabels(dep) 633 } 634 } 635 } 636 } 637 getLabels(target) 638 ret := make([]string, len(labels)) 639 i := 0 640 for label := range labels { 641 ret[i] = label 642 i++ 643 } 644 sort.Strings(ret) 645 return fromStringList(ret) 646 } 647 648 // getTargetPost is called by various functions to get a target from the current package. 649 // Panics if the target is not in the current package or has already been built. 650 func getTargetPost(s *scope, name string) *core.BuildTarget { 651 target := s.pkg.Target(name) 652 s.Assert(target != nil, "Unknown build target %s in %s", name, s.pkg.Name) 653 // It'd be cheating to try to modify targets that're already built. 654 // Prohibit this because it'd likely end up with nasty race conditions. 655 s.Assert(target.State() < core.Built, "Attempted to modify target %s, but it's already built", target.Label) 656 return target 657 } 658 659 // addDep adds a dependency to a target. 660 func addDep(s *scope, args []pyObject) pyObject { 661 s.Assert(s.Callback, "can only be called from a pre- or post-build callback") 662 target := getTargetPost(s, string(args[0].(pyString))) 663 dep := core.ParseBuildLabel(string(args[1].(pyString)), s.pkg.Name) 664 exported := args[2].IsTruthy() 665 target.AddMaybeExportedDependency(dep, exported, false) 666 // Note that here we're in a post-build function so we must call this explicitly 667 // (in other callbacks it's handled after the package parses all at once). 668 s.state.Graph.AddDependency(target.Label, dep) 669 s.pkg.MarkTargetModified(target) 670 return None 671 } 672 673 // addOut adds an output to a target. 674 func addOut(s *scope, args []pyObject) pyObject { 675 target := getTargetPost(s, string(args[0].(pyString))) 676 name := string(args[1].(pyString)) 677 out := string(args[2].(pyString)) 678 if out == "" { 679 target.AddOutput(name) 680 s.pkg.MustRegisterOutput(name, target) 681 } else { 682 target.AddNamedOutput(name, out) 683 s.pkg.MustRegisterOutput(out, target) 684 } 685 return None 686 } 687 688 // addLicence adds a licence to a target. 689 func addLicence(s *scope, args []pyObject) pyObject { 690 target := getTargetPost(s, string(args[0].(pyString))) 691 target.AddLicence(string(args[1].(pyString))) 692 return None 693 } 694 695 // getCommand gets the command of a target, optionally for a configuration. 696 func getCommand(s *scope, args []pyObject) pyObject { 697 target := getTargetPost(s, string(args[0].(pyString))) 698 return pyString(target.GetCommandConfig(string(args[1].(pyString)))) 699 } 700 701 // setCommand sets the command of a target, optionally for a configuration. 702 func setCommand(s *scope, args []pyObject) pyObject { 703 target := getTargetPost(s, string(args[0].(pyString))) 704 config := string(args[1].(pyString)) 705 command := string(args[2].(pyString)) 706 if command == "" { 707 target.Command = config 708 } else { 709 target.AddCommand(config, command) 710 } 711 return None 712 } 713 714 // selectFunc implements the select() builtin. 715 func selectFunc(s *scope, args []pyObject) pyObject { 716 d, _ := asDict(args[0]) 717 var def pyObject 718 pkgName := "" 719 if s.pkg != nil { 720 pkgName = s.pkg.Name 721 } 722 // TODO(peterebden): this is an arbitrary match that drops Bazel's order-of-matching rules. Fix. 723 for k, v := range d { 724 if k == "//conditions:default" || k == "default" { 725 def = v 726 } else if selectTarget(s, core.ParseBuildLabel(k, pkgName)).HasLabel("config:on") { 727 return v 728 } 729 } 730 s.NAssert(def == nil, "None of the select() conditions matched") 731 return def 732 } 733 734 // selectTarget returns the target to be used for a select() call. 735 // It panics appropriately if the target isn't built yet. 736 func selectTarget(s *scope, l core.BuildLabel) *core.BuildTarget { 737 if s.pkg != nil && l.PackageName == s.pkg.Name { 738 t := s.pkg.Target(l.Name) 739 s.NAssert(t == nil, "Target %s in select() call has not been defined yet", l.Name) 740 return t 741 } 742 return subincludeTarget(s, l) 743 } 744 745 // subrepo implements the subrepo() builtin that adds a new repository. 746 func subrepo(s *scope, args []pyObject) pyObject { 747 root := func(def string) string { 748 if args[2] != None { 749 return string(args[2].(pyString)) 750 } 751 return def 752 } 753 754 name := string(args[0].(pyString)) 755 dep := string(args[1].(pyString)) 756 if dep == "" { 757 // This is deliberately different to facilitate binding subrepos within the same VCS repo. 758 s.state.Graph.AddSubrepo(&core.Subrepo{Name: name, Root: root(name)}) 759 return None 760 } 761 // N.B. The target must be already registered on this package. 762 t := s.pkg.TargetOrDie(core.ParseBuildLabel(dep, s.pkg.Name).Name) 763 s.state.Graph.AddSubrepo(&core.Subrepo{ 764 Name: name, 765 Root: root(path.Join(t.OutDir(), name)), 766 Target: t, 767 }) 768 log.Debug("Registered subrepo %s", name) 769 return None 770 }