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