github.phpd.cn/thought-machine/please@v12.2.0+incompatible/src/parse/rules/go_rules.build_defs (about) 1 """ Rules to build Go code. 2 3 Go has a strong built-in concept of packages so it's probably a good idea to match Please 4 rules to Go packages. 5 """ 6 7 _GOPATH = ' '.join(['-I %s -I %s/pkg/%s_%s' % (p, p, CONFIG.OS, CONFIG.ARCH) for p in CONFIG.GOPATH.split(':')]) 8 # This links all the .a files up one level. This is necessary for some Go tools to find them. 9 _LINK_PKGS_CMD = ' '.join([ 10 'for FN in `find . -name "*.a" | sort`; do ', 11 'DN=${FN%/*}; BN=${FN##*/}; if [ "${DN##*/}" == "${BN%.a}" ]; then ', 12 'ln -s $TMP_DIR/$FN ${DN%/*}; fi; done' 13 ]) 14 15 16 def go_library(name:str, srcs:list, asm_srcs:list=None, hdrs:list=None, out:str=None, deps:list=[], 17 visibility:list=None, test_only:bool&testonly=False, go_tools:list=None, complete:bool=True, 18 _needs_transitive_deps=False, _all_srcs=False, cover:bool=True, filter_srcs:bool=True): 19 """Generates a Go library which can be reused by other rules. 20 21 Args: 22 name (str): Name of the rule. 23 srcs (list): Go source files to compile. 24 asm_srcs (list): Source files to assemble with `go tool assemble`. 25 hdrs (list): Header files needed for assembly. Has no effect if asm_srcs is not given. 26 out (str): Name of the output library to compile (defaults to name suffixed with .a) 27 deps (list): Dependencies 28 visibility (list): Visibility specification 29 test_only (bool): If True, is only visible to test rules. 30 go_tools (list): A list of targets to pre-process your src files with go generate. 31 complete (bool): Indicates whether the library is complete or not (ie. buildable with 32 `go tool build -complete`). In nearly all cases this is True (the main 33 exception being for cgo). 34 cover (bool): Indicates whether this library should be considered for coverage annotations. 35 Libraries are only annotated when using `plz cover` (or `plz build -c cover`), 36 but if this is false they never will be. Can be useful for e.g. third-party 37 code that you never want to be instrumented. 38 filter_srcs (bool): If True, filters source files through Go's standard build constraints. 39 """ 40 if asm_srcs: 41 lib_rule = go_library( 42 name = '_%s#lib' % name, 43 srcs = srcs, 44 deps = deps, 45 test_only = test_only, 46 go_tools = go_tools, 47 complete = False, 48 cover = cover, 49 _needs_transitive_deps = _needs_transitive_deps, 50 _all_srcs = _all_srcs, 51 ) 52 asm_rule = build_rule( 53 name = name, 54 tag = 'asm', 55 srcs = { 56 'asm': asm_srcs, 57 'hdrs': hdrs, 58 }, 59 outs = [name + '.o'], 60 building_description = 'Assembling...', 61 cmd = 'eval `$TOOL env` && $TOOL tool asm -trimpath $TMP_DIR -I ${GOROOT}/pkg/include -D GOOS_${OS} -D GOARCH_${ARCH} -o $OUT $SRCS_ASM', 62 tools=[CONFIG.GO_TOOL], 63 test_only = test_only, 64 ) 65 return build_rule( 66 name = name, 67 srcs = { 68 'lib': [lib_rule], 69 'asm': [asm_rule], 70 }, 71 outs=[out or name + '.a'], 72 tools=[CONFIG.GO_TOOL], 73 cmd = 'cp $SRCS_LIB $OUT && chmod +w $OUT && $TOOL tool pack r $OUT $SRCS_ASM', 74 visibility = visibility, 75 building_description = 'Packing...', 76 requires = ['go'], 77 provides = {'go': ':' + name, 'go_src': lib_rule}, 78 test_only = test_only, 79 ) 80 81 # go_test and cgo_library need access to the sources as well. 82 src_rule = filegroup( 83 name = name, 84 tag = 'srcs', 85 srcs=srcs, 86 exported_deps=deps, 87 visibility=visibility, 88 requires=['go'], 89 test_only=test_only, 90 ) 91 92 # Run go generate if needed. 93 if go_tools: 94 gen_rule = go_generate( 95 name='_%s#gen' % name, 96 srcs=srcs, 97 tools=go_tools, 98 deps=deps + [src_rule], 99 test_only=test_only, 100 ) 101 srcs += [gen_rule] 102 103 tools = { 'go': [CONFIG.GO_TOOL] } 104 if filter_srcs: 105 tools['filter'] = [CONFIG.GO_FILTER_TOOL] 106 107 return build_rule( 108 name=name, 109 srcs=srcs, 110 deps=deps + [src_rule], 111 outs=[out or name + '.a'], 112 cmd=_go_library_cmds(complete=complete, all_srcs=_all_srcs, cover=cover, filter_srcs=filter_srcs), 113 visibility=visibility, 114 building_description="Compiling...", 115 requires=['go', 'go_src'] if _all_srcs else ['go'], 116 provides={'go': ':' + name, 'go_src': src_rule}, 117 test_only=test_only, 118 tools=tools, 119 needs_transitive_deps=_needs_transitive_deps, 120 ) 121 122 123 def go_generate(name:str, srcs:list, tools:list, deps:list=None, visibility:list=None, test_only:bool&testonly=False): 124 """Generates a `go generate` rule. 125 126 Deprecated, use https://github.com/thought-machine/pleasings/blob/master/go/go_generate.build_defs instead. 127 This will probably be removed in plz v13+. 128 129 Args: 130 name (str): Name of the rule. 131 srcs (list): Go source files to run go generate over. 132 tools (list): A list of targets which represent binaries to be used via `go generate`. 133 deps (list): Dependencies 134 visibility (list): Visibility specification 135 test_only (bool): If True, is only visible to test rules. 136 """ 137 log.warning('The builtin go_generate is deprecated, see https://github.com/thought-machine/pleasings/blob/master/go/go_generate.build_defs for an alternative.') 138 139 # We simply capture all go files produced by go generate. 140 def _post_build(rule_name, output): 141 for out in output: 142 if out.endswith('.go') and srcs and out not in srcs: 143 add_out(rule_name, out) 144 145 # All the tools must be in the $PATH. 146 cmd = ' && '.join([ 147 # It's essential that we copy all .a files up a directory as well; we tend to output them one level 148 # down from where Go expects them to be. 149 _LINK_PKGS_CMD, 150 # It's also essential that the compiled .a files are under this prefix, otherwise gcimporter won't find them. 151 'mkdir pkg', 152 'ln -s . src', 153 'ln -s $TMP_DIR pkg/%s_%s' % (CONFIG.OS, CONFIG.ARCH), 154 'export PATH="$(echo "$TOOLS_GEN " | sed -E -e \'s|/[^/]+[ ]|:|g\')$PATH"', 155 'GOPATH="%s" $TOOLS_GO generate $SRCS' % CONFIG.GOPATH, 156 'mv $PKG_DIR/*.go .', 157 'ls *.go' 158 ]) 159 return build_rule( 160 name=name, 161 srcs=srcs, 162 deps=deps, 163 tools={ 164 'go': [CONFIG.GO_TOOL], 165 'gen': tools, 166 }, 167 cmd=cmd, 168 visibility=visibility, 169 test_only=test_only, 170 post_build=_post_build, 171 requires = ['go', 'go_src'], 172 ) 173 174 175 def cgo_library(name:str, srcs:list, go_srcs:list=[], c_srcs:list=[], hdrs:list=[], 176 out:str=None, compiler_flags:list&cflags=[], linker_flags:list&ldflags=[], pkg_config:list=[], 177 subdir:str='', deps:list=[], visibility:list=None, test_only:bool&testonly=False): 178 """Generates a Go library which can be reused by other rules. 179 180 Note that by its nature this is something of a hybrid of Go and C rules. It can depend 181 on C / C++ rules, given the limitations of cgo (i.e. you will have to interact with them 182 through a C interface, although the objects themselves can contain C++). As mentioned 183 below, you will likely be better off wrapping your dependencies into a cc_static_library 184 rule and depending on that rather than depending directly on cc_library rules. 185 186 Note also that this does not honour Go's syntactic comments; you have to explicitly 187 specify which Go files are cgo vs. which are not, as well as C headers & sources and 188 any required cflags or ldflags. 189 190 Args: 191 name (str): Name of the rule. 192 srcs (list): Go source files to compile that have 'import "C"' declarations in them. 193 go_srcs (list): Any Go source files that do *not* have 'import "C"' declarations. 194 c_srcs (list): Any C source files to include. 195 hdrs (list): Any C header files to include. 196 out (str): Name of output file. Defaults to name + '.a'. 197 compiler_flags (list): List of compiler flags to be passed when compiling the C code. 198 linker_flags (list): List of linker flags to be passed when linking a Go binary. 199 pkg_config (list): List of packages to pass to pkg-config. 200 subdir (str): Subdirectory that source files are in. Required if they're not in the 201 current directory. 202 deps (list): Dependencies. Note that if you intend to depend on cc_library rules, 203 you will likely be better off wrapping them into a cc_static_library 204 and depending on that. 205 visibility (list): Visibility specification 206 test_only (bool): If True, is only visible to test rules. 207 """ 208 file_srcs = [src for src in srcs if not src.startswith('/') and not src.startswith(':')] 209 post_build = lambda rule, output: [add_out(rule, 'c' if line.endswith('.c') else 'go', line) for line in output] 210 subdir2 = (subdir + '/') if subdir and not subdir.endswith('/') else subdir 211 212 cgo_rule = build_rule( 213 name = name, 214 tag = 'cgo', 215 srcs = srcs + hdrs, 216 outs = { 217 'go': [subdir2 + src.replace('.go', '.cgo1.go') for src in file_srcs] + [subdir2 + '_cgo_gotypes.go'], 218 'c': [subdir2 + src.replace('.go', '.cgo2.c') for src in file_srcs] + [subdir2 + '_cgo_export.c'], 219 'h': [subdir2 + '_cgo_export.h'], 220 }, 221 cmd = ' && '.join([ 222 ('OUT_DIR="$TMP_DIR/%s"' % subdir) if subdir else 'OUT_DIR="$TMP_DIR"', 223 'mkdir -p $OUT_DIR', 224 'cd $PKG_DIR/' + subdir, 225 '$TOOL tool cgo -objdir $OUT_DIR -importpath ${PKG#*src/} *.go', 226 # Remove the .o file which BSD sed gets upset about in the next command 227 'rm -f $OUT_DIR/_cgo_.o $OUT_DIR/_cgo_main.c', 228 # cgo leaves absolute paths in these files which we must get rid of :( 229 'find $OUT_DIR -type f -maxdepth 1 | xargs sed -i -e "s|$TMP_DIR/||g"', 230 'cd $TMP_DIR', 231 'ls %s*.c %s*.go' % (subdir2, subdir2), 232 ]), 233 tools = [CONFIG.GO_TOOL], 234 post_build = post_build if file_srcs != srcs else None, 235 requires = ['go', 'cc_hdrs'], 236 ) 237 238 # Compile the various bits 239 c_rule = c_library( 240 name = '_%s#c' % name, 241 srcs = [cgo_rule + '|c'] + c_srcs, 242 hdrs = [cgo_rule + '|h'] + hdrs, 243 compiler_flags = compiler_flags + [ 244 '-Wno-error', 245 '-Wno-unused-parameter', # Generated code doesn't compile clean 246 ], 247 pkg_config_libs = pkg_config, 248 test_only = test_only, 249 deps = deps, 250 ) 251 go_rule = go_library( 252 name = '_%s#go' % name, 253 srcs = [cgo_rule + '|go'] + go_srcs, 254 test_only = test_only, 255 complete = False, 256 deps = deps, 257 ) 258 # And finally combine the compiled C code into the Go archive object so go tool link can find it later. 259 return _merge_cgo_obj( 260 name = name, 261 a_rule = ':_%s#go' % name, 262 o_rule = c_rule, 263 visibility = visibility, 264 test_only = test_only, 265 linker_flags = linker_flags, 266 out=out, 267 provides = { 268 'go': ':' + name, 269 'go_src': go_rule, 270 'cgo': c_rule, 271 }, 272 deps = deps, 273 ) 274 275 276 def _merge_cgo_obj(name, a_rule, o_rule=None, visibility=None, test_only=False, tag='', 277 linker_flags=None, deps=None, provides=None, out=None): 278 """Defines a rule to merge a cgo object into a Go library.""" 279 if o_rule: 280 cmd = 'cp $SRCS_A $OUT && chmod +w $OUT && $TOOLS_AR x $SRCS_O && $TOOLS_GO tool pack r $OUT *.o' 281 else: 282 cmd = 'cp $SRCS_A $OUT && chmod +w $OUT && $TOOLS_AR x $PKG/*#c.a && $TOOLS_GO tool pack r $OUT *.o' 283 284 return build_rule( 285 name = name, 286 tag = tag, 287 srcs = { 288 'a': [a_rule], 289 'o': [o_rule] if o_rule else [], 290 }, 291 outs = [out or name + '.a'], 292 cmd = cmd, 293 tools = { 294 'go': [CONFIG.GO_TOOL], 295 'ar': [CONFIG.AR_TOOL], 296 }, 297 visibility = visibility, 298 test_only = test_only, 299 labels = ['cc:ld:' + flag for flag in (linker_flags or [])], 300 requires = ['go', 'cgo'], 301 provides = provides, 302 deps = deps, 303 ) 304 305 306 def go_binary(name:str, srcs:list=[], asm_srcs:list=[], out:str=None, deps:list=[], 307 visibility:list=None, test_only:bool&testonly=False, static:bool=CONFIG.GO_DEFAULT_STATIC, 308 filter_srcs:bool=True): 309 """Compiles a Go binary. 310 311 Args: 312 name (str): Name of the rule. 313 srcs (list): Go source files, one of which contains the main function. 314 asm_srcs (list): Assembly source files. 315 out (str): Name of the output file to create. Defaults to the same as `name`. 316 deps (list): Dependencies 317 visibility (list): Visibility specification 318 test_only (bool): If True, is only visible to test rules. 319 static (bool): If True, passes flags to the linker to try to force fully static linking. 320 (specifically `-linkmode external -extldflags static`). 321 Typically this increases size & link time a little but in return the binary 322 has absolutely no external dependencies. 323 Note that it may have negative consequences if the binary contains any cgo 324 (including net/http DNS lookup code potentially). 325 filter_srcs (bool): If True, filters source files through Go's standard build constraints. 326 """ 327 go_library( 328 name='_%s#lib' % name, 329 srcs=srcs or [name + '.go'], 330 filter_srcs=filter_srcs, 331 asm_srcs=asm_srcs, 332 deps=deps, 333 test_only=test_only, 334 ) 335 cmds, tools = _go_binary_cmds(static=static) 336 return build_rule( 337 name=name, 338 srcs=[':_%s#lib' % name], 339 deps=deps, 340 outs=[out or name], 341 cmd=cmds, 342 building_description="Linking...", 343 needs_transitive_deps=True, 344 binary=True, 345 output_is_complete=True, 346 test_only=test_only, 347 tools=tools, 348 visibility=visibility, 349 requires=['go'], 350 pre_build=_collect_linker_flags(static), 351 ) 352 353 354 def go_test(name:str, srcs:list, data:list=None, deps:list=[], visibility:list=None, 355 flags:str='', container:bool|dict=False, sandbox:bool=None, cgo:bool=False, 356 external:bool=False, timeout:int=0, flaky:bool|int=0, test_outputs:list=None, 357 labels:list&features&tags=None, size:str=None, static:bool=CONFIG.GO_DEFAULT_STATIC): 358 """Defines a Go test rule. 359 360 Args: 361 name (str): Name of the rule. 362 srcs (list): Go source files to compile. 363 data (list): Runtime data files for the test. 364 deps (list): Dependencies 365 visibility (list): Visibility specification 366 flags (str): Flags to apply to the test invocation. 367 container (bool | dict): True to run this test in a container. 368 sandbox (bool): Sandbox the test on Linux to restrict access to namespaces such as network. 369 cgo (bool): True if this test depends on a cgo_library. 370 external (bool): True if this test is external to the library it's testing, i.e. it uses the 371 feature of Go that allows it to be in the same directory with a _test suffix. 372 timeout (int): Timeout in seconds to allow the test to run for. 373 flaky (int | bool): True to mark the test as flaky, or an integer to specify how many reruns. 374 test_outputs (list): Extra test output files to generate from this test. 375 labels (list): Labels for this rule. 376 size (str): Test size (enormous, large, medium or small). 377 static (bool): If True, passes flags to the linker to try to force fully static linking. 378 (specifically `-linkmode external -extldflags static`). 379 Typically this increases size & link time a little but in return the binary 380 has absolutely no external dependencies. 381 Note that it may have negative consequences if the binary contains any cgo 382 (including net/http DNS lookup code potentially). 383 """ 384 timeout, labels = _test_size_and_timeout(size, timeout, labels) 385 # Unfortunately we have to recompile this to build the test together with its library. 386 lib_rule = go_library( 387 name = '_%s#lib' % name, 388 srcs = srcs, 389 out = name + ('_lib.a' if cgo else '.a'), 390 deps = deps, 391 test_only = True, 392 _all_srcs = not external, 393 complete = False, 394 ) 395 if cgo: 396 lib_rule = _merge_cgo_obj( 397 name = name, 398 tag = 'cgo', 399 a_rule = lib_rule, 400 visibility = visibility, 401 test_only = True, 402 deps = deps, 403 ) 404 build_rule( 405 name='_%s#main' % name, 406 srcs=srcs, 407 outs=[name + '_main.go'], 408 deps=deps, 409 cmd={ 410 'dbg': '$TOOLS_TEST $TOOLS_GO -o $OUT $SRCS', 411 'opt': '$TOOLS_TEST $TOOLS_GO -o $OUT $SRCS', 412 'cover': '$TOOLS_TEST $TOOLS_GO -d . -o $OUT $SRCS ', 413 }, 414 needs_transitive_deps=True, # Need all .a files to template coverage variables 415 requires=['go'], 416 test_only=True, 417 tools={ 418 'go': [CONFIG.GO_TOOL], 419 'test': [CONFIG.GO_TEST_TOOL], 420 }, 421 post_build=lambda name, output: _replace_test_package(name, output, static), 422 ) 423 deps.append(lib_rule) 424 go_library( 425 name='_%s#main_lib' % name, 426 srcs=[':_%s#main' % name], 427 deps=deps, 428 _needs_transitive_deps=True, # Rather annoyingly this is only needed for coverage 429 test_only=True, 430 ) 431 cmds, tools = _go_binary_cmds(static=static) 432 build_rule( 433 name=name, 434 srcs=[':_%s#main_lib' % name], 435 data=data, 436 deps=deps, 437 outs=[name], 438 tools=tools, 439 cmd=cmds, 440 test_cmd='$TEST %s | tee test.results' % flags, 441 visibility=visibility, 442 container=container, 443 test_sandbox=sandbox, 444 test_timeout=timeout, 445 flaky=flaky, 446 test_outputs=test_outputs, 447 requires=['go'], 448 labels=labels, 449 binary=True, 450 test=True, 451 building_description="Compiling...", 452 needs_transitive_deps=True, 453 output_is_complete=True, 454 ) 455 456 457 def cgo_test(name:str, srcs:list, data:list=None, deps:list=None, visibility:list=None, 458 flags:str='', container:bool|dict=False, sandbox:bool=None, timeout:int=0, flaky:bool|int=0, 459 test_outputs:list=None, labels:list&features&tags=None, size:str=None, static:bool=False): 460 """Defines a Go test rule over a cgo_library. 461 462 If the library you are testing is a cgo_library, you must use this instead of go_test. 463 It's ok to depend on a cgo_library though as long as it's not the same package 464 as your test; in that (any any other case of testing a go_library) you must use go_test. 465 466 Args: 467 name (str): Name of the rule. 468 srcs (list): Go source files to compile. 469 data (list): Runtime data files for the test. 470 deps (list): Dependencies 471 visibility (list): Visibility specification 472 flags (str): Flags to apply to the test invocation. 473 container (bool | dict): True to run this test in a container. 474 sandbox (bool): Sandbox the test on Linux to restrict access to namespaces such as network. 475 timeout (int): Timeout in seconds to allow the test to run for. 476 flaky (int | bool): True to mark the test as flaky, or an integer to specify how many reruns. 477 test_outputs (list): Extra test output files to generate from this test. 478 labels (list): Labels for this rule. 479 size (str): Test size (enormous, large, medium or small). 480 static (bool): If True, passes flags to the linker to try to force fully static linking. 481 (specifically `-linkmode external -extldflags static`). 482 Typically this increases size & link time a little but in return the binary 483 has absolutely no external dependencies. 484 It may not be easy to make cgo tests work when linked statically; depending 485 on your toolchain it may not be possible or may fail. 486 """ 487 go_test( 488 name = name, 489 srcs = srcs, 490 data = data, 491 deps = deps, 492 cgo = True, 493 static = static, 494 visibility = visibility, 495 flags = flags, 496 container = container, 497 sandbox = sandbox, 498 timeout = timeout, 499 flaky = flaky, 500 test_outputs = test_outputs, 501 labels = labels, 502 size = size, 503 ) 504 505 506 def go_get(name:str, get:str|list, repo:str='', deps:list=[], exported_deps:list=None, 507 visibility:list=None, patch:str=None, binary:bool=False, test_only:bool&testonly=False, 508 install:list=None, revision:str|list=None, strip:list=None, hashes:list=None, 509 extra_outs:list=[]): 510 """Defines a dependency on a third-party Go library. 511 512 Note that unlike a normal `go get` call, this does *not* install transitive dependencies. 513 You will need to add those as separate rules; `go list -f '{{.Deps}}' <package>` can be 514 useful to discover what they should be. 515 516 Note also that while a single go_get is sufficient to compile all parts of a library, 517 one may also want separate ones for a binary. Since two rules can't both output the same 518 source files (and you only want to download them once anyway) you should handle that by 519 marking the non-binary rule as a dependency of the binary one - if you don't there may 520 be warnings issued about conflicting output files. 521 522 Args: 523 name (str): Name of the rule 524 get (str): Target to get (eg. "github.com/gorilla/mux"). Can also be a list of multiple in 525 which case they are fetched separately and compiled together, which can be useful 526 for avoiding issues with circular dependencies. 527 repo (str): Location of a Git repository to fetch from. 528 deps (list): Dependencies 529 exported_deps (list): Dependencies to make available to anything using this rule. 530 visibility (list): Visibility specification 531 patch (str): Patch file to apply 532 binary (bool): True if the output of the rule is a binary. 533 test_only (bool): If true this rule will only be visible to tests. 534 install (list): Allows specifying the exact list of packages to install. If this is not passed, 535 the package passed to 'get' is installed. If you pass this for subpackages, you 536 will need to explicitly add an empty string to the list if you want to install 537 the root package from this repo. 538 revision (str): Git hash to check out before building. Only works for git at present, 539 not for other version control systems. 540 strip (list): List of paths to strip from the installed target. 541 hashes (list): List of hashes to verify the downloaded sources against. 542 extra_outs (list): List of additional output files after the compile. 543 """ 544 if isinstance(get, str): 545 get = [get] 546 revision = [revision] 547 tags = ['get'] 548 else: 549 tags = [basename(dirname(g)) for g in get] 550 revision = revision or [None for g in get] 551 all_installs = [] 552 outs = extra_outs 553 provides = None 554 for getone, revision, tag in zip(get, revision, tags): 555 get_rule, getroot = _go_get_download( 556 name = name, 557 tag = tag, 558 get = getone, 559 repo = repo, 560 patch = patch, 561 hashes = hashes, 562 test_only = test_only, 563 revision = revision, 564 strip = strip, 565 ) 566 outs += [getroot] 567 deps += [get_rule] 568 provides = {'go': ':' + name, 'go_src': get_rule} 569 if install: 570 all_installs += [i if i.startswith(getroot) else (getroot + '/' + i) for i in install] 571 else: 572 all_installs += [getone] 573 574 # Now compile it in a separate step. 575 cmd = [ 576 'export GOPATH="%s"' % CONFIG.GOPATH, 577 '$TOOL install -gcflags "-trimpath $TMP_DIR" ' + ' '.join(all_installs or install), 578 ] 579 if package_name(): 580 cmd += [ 581 # The outputs tend to end up in subdirectories (Go seems to match them to the location the source came from) 582 'rm -rf bin' if binary else 'rm -rf pkg', 583 'mv $PKG_DIR/bin .' if binary else 'mv $PKG_DIR/pkg .', 584 ] 585 if binary: 586 outs = ['bin/' + name] 587 else: 588 # Outputs are created one directory down from where we want them. 589 # For most it doesn't matter but the top-level one will get lost. 590 cmd += [' if [ -f ${OUTS}.a ]; then mkdir -p $OUTS && mv ${OUTS}.a $OUTS; fi'] 591 outs = ['pkg/%s_%s/%s' % (CONFIG.OS, CONFIG.ARCH, out) for out in outs] 592 return build_rule( 593 name = name, 594 outs = outs, 595 deps = deps, 596 exported_deps = exported_deps, 597 tools = [CONFIG.GO_TOOL], 598 visibility = visibility, 599 building_description = 'Compiling...', 600 cmd = ' && '.join(cmd), 601 binary = binary, 602 requires = ['go'], 603 test_only = test_only, 604 sandbox = False, 605 needs_transitive_deps = True, 606 provides = provides, 607 ) 608 609 610 def _go_get_download(name:str, tag:str, get:str, repo:str='', patch:str=None, hashes:list=None, 611 test_only:bool&testonly=False, revision:str=None, strip:list=None, 612 labels:list=[]): 613 if hashes and not revision: 614 log.warning("You shouldn't specify hashes on go_get without specifying revision as well") 615 labels = ['go_get:%s@%s' % (get, revision) if revision else 'go_get:%s' % get] 616 getroot = get[:-4] if get.endswith('/...') else get 617 subdir = 'src/' + getroot 618 619 # Some special optimisation for github, which lets us download zipfiles at a particular sha instead of 620 # cloning the whole repo. Obviously that is a lot faster than cloning entire repos. 621 if get.startswith('github.com') and revision and get.count('/') >= 2: 622 cmd, get_deps, tools = _go_github_repo_cmd(name, getroot, getroot, revision) 623 elif get.startswith('golang.org/x/') and revision and not repo: 624 # We know about these guys... 625 cmd, get_deps, tools = _go_github_repo_cmd(name, getroot, 'github.com/golang/' + getroot[len('golang.org/x/'):], revision) 626 else: 627 get_deps = [] 628 if repo: 629 # we've been told exactly where to get the source from. 630 cmd = ['git clone %s src/%s' % (repo, getroot)] 631 else: 632 # Ultimate fallback to go get. 633 # This has some more restrictions than the above (e.g. go get won't fetch a directory 634 # with no Go files in it, even if passed -d). 635 cmd = [ 636 'rm -rf src pkg', 637 'export GOPATH="$TMP_DIR"', 638 '$TOOL get -d ' + get, 639 ] 640 if revision: 641 # Annoyingly -C does not work on git checkout :( 642 cmd.append('(cd %s && git checkout -q %s)' % (subdir, revision)) 643 cmd.append('find . -name .git | xargs rm -rf') 644 tools = [CONFIG.GO_TOOL] 645 if patch: 646 cmd.append('patch -s -d %s -p1 < ${TMP_DIR}/$SRCS_PATCH' % subdir) 647 if strip: 648 cmd += ['rm -rf %s/%s' % (subdir, s) for s in strip] 649 return build_rule( 650 name = name, 651 tag = tag, 652 srcs = { 653 'patch': [patch], 654 'get': get_deps, 655 }, 656 outs = [subdir], 657 tools = tools, 658 building_description = 'Fetching...', 659 cmd = ' && '.join(cmd), 660 requires = ['go'], 661 test_only = test_only, 662 labels = labels, 663 hashes = hashes, 664 sandbox = False, 665 ), getroot 666 667 668 def _go_github_repo_cmd(name, get, repo, revision): 669 """Returns a partial command to fetch a Go repo from Github.""" 670 parts = get.split('/') 671 out = '/'.join(parts[:3]) 672 if repo.count('/') > 2: 673 parts = repo.split('/') 674 repo = '/'.join(parts[:3]) 675 remote_rule = remote_file( 676 name = name, 677 _tag = 'download', 678 url = 'https://%s/archive/%s.zip' % (repo, revision), 679 out = name + '.zip', 680 ) 681 return [ 682 'rm -rf src/' + out, 683 '$TOOL x $SRCS_GET', 684 'mv %s* src/%s' % (parts[2], out), 685 ], [remote_rule], [CONFIG.JARCAT_TOOL] 686 687 688 def _replace_test_package(name, output, static): 689 """Post-build function, called after we template the main function. 690 691 The purpose is to replace the real library with the specific one we've 692 built for this test which has the actual test functions in it. 693 """ 694 if not name.endswith('#main') or not name.startswith('_'): 695 raise ValueError('unexpected rule name: ' + name) 696 lib = name[:-5] + '#main_lib' 697 new_name = name[1:-5] 698 for line in output: 699 if line.startswith('Package: '): 700 ldflags, pkg_config = _get_ldflags_and_pkgconfig(name) 701 pkg_name = line[9:] 702 name_changed = pkg_name != new_name 703 if name_changed or ldflags or pkg_config: # Might not be necessary if names match already. 704 binary_cmds, _ = _go_binary_cmds(static=static, ldflags=ldflags, pkg_config=pkg_config) 705 if name_changed: 706 for k, v in binary_cmds.items(): 707 set_command(new_name, k, 'mv -f ${PKG_DIR}/%s.a ${PKG_DIR}/%s.a && %s' % (new_name, pkg_name, v)) 708 else: 709 for k, v in binary_cmds.items(): 710 set_command(new_name, k, v) 711 if name_changed: 712 for k, v in _go_library_cmds().items(): 713 set_command(lib, k, 'mv -f ${PKG_DIR}/%s.a ${PKG_DIR}/%s.a && %s' % (new_name, pkg_name, v)) 714 715 716 def _go_library_cmds(complete=True, all_srcs=False, cover=True, filter_srcs=True): 717 """Returns the commands to run for building a Go library.""" 718 filter_cmd = 'export SRCS="$(${TOOLS_FILTER} ${SRCS})"; ' if filter_srcs else '' 719 # Invokes the Go compiler. 720 complete_flag = '-complete ' if complete else '' 721 compile_cmd = '$TOOLS_GO tool compile -trimpath $TMP_DIR %s%s -pack -o $OUT ' % (complete_flag, _GOPATH) 722 # Annotates files for coverage. 723 cover_cmd = 'for SRC in $SRCS; do BN=$(basename $SRC); go tool cover -mode=set -var=GoCover_${BN//[.-]/_} $SRC > _tmp.go && mv -f _tmp.go $SRC; done' 724 prefix = ('export SRCS="$PKG_DIR/*.go"; ' + _LINK_PKGS_CMD) if all_srcs else _LINK_PKGS_CMD 725 prefix += _go_import_path_cmd(CONFIG.GO_IMPORT_PATH) 726 cmds = { 727 'dbg': '%s; %s%s -N -l $SRCS' % (prefix, filter_cmd, compile_cmd), 728 'opt': '%s; %s%s $SRCS' % (prefix, filter_cmd, compile_cmd), 729 } 730 if cover: 731 cmds['cover'] = '%s; %s%s && %s $SRCS' % (prefix, filter_cmd, cover_cmd, compile_cmd) 732 return cmds 733 734 735 def _go_binary_cmds(static=False, ldflags='', pkg_config=''): 736 """Returns the commands to run for linking a Go binary.""" 737 _link_cmd = '$TOOLS_GO tool link -tmpdir $TMP_DIR -extld $TOOLS_LD %s -L . -o ${OUT} ' % _GOPATH.replace('-I ', '-L ') 738 prefix = _LINK_PKGS_CMD + _go_import_path_cmd(CONFIG.GO_IMPORT_PATH) 739 740 if static: 741 flags = '-linkmode external -extldflags "-static %s %s"' % (ldflags, pkg_config) 742 elif ldflags or pkg_config: 743 flags = '-extldflags "%s %s"' % (ldflags, pkg_config) 744 else: 745 flags = '' 746 747 return { 748 'dbg': '%s && %s %s $SRCS' % (prefix, _link_cmd, flags), 749 'opt': '%s && %s %s -s -w $SRCS' % (prefix, _link_cmd, flags), 750 }, { 751 'go': [CONFIG.GO_TOOL], 752 'ld': [CONFIG.LD_TOOL if CONFIG.LINK_WITH_LD_TOOL else CONFIG.CC_TOOL], 753 } 754 755 756 def _go_import_path_cmd(import_path): 757 """Returns a partial command which is used for setting up the Go import path.""" 758 if not import_path: 759 return '' 760 elif import_path.startswith('/'): 761 raise ConfigError('GO_IMPORT_PATH cannot start with a /') 762 elif '/' in import_path: 763 return ' && mkdir -p %s && ln -s $TMP_DIR %s' % (dirname(import_path), import_path) 764 else: 765 return ' && ln -s $TMP_DIR ' + import_path 766 767 768 def _collect_linker_flags(static): 769 """Returns a pre-build function to apply transitive linker flags to a go_binary rule.""" 770 def collect_linker_flags(name): 771 ldflags, pkg_config = _get_ldflags_and_pkgconfig(name) 772 if ldflags or pkg_config: 773 cmds, _ = _go_binary_cmds(static=static, ldflags=ldflags, pkg_config=pkg_config) 774 for k, v in cmds.items(): 775 set_command(name, k, v) 776 return collect_linker_flags 777 778 779 def _get_ldflags_and_pkgconfig(name): 780 """Returns the ldflags and pkg-config invocations for a target.""" 781 labels = get_labels(name, 'cc:') 782 ldflags = ' '.join([l[3:] for l in labels if l.startswith('ld:')]) 783 pkg_config = ' '.join([l[3:] for l in labels if l.startswith('pc:')]) 784 return (ldflags, '`pkg-config --libs %s`' % pkg_config) if pkg_config else (ldflags, '')