github.phpd.cn/thought-machine/please@v12.2.0+incompatible/src/parse/rules/python_rules.build_defs (about) 1 """ Rules to build Python code. 2 3 The output artifacts for Python rules are .pex files (see https://github.com/pantsbuild/pex). 4 Pex is a rather nice system for combining Python code and all needed dependencies 5 (excluding the actual interpreter and possibly some system level bits) into a single file. 6 7 The process of compiling pex files can be a little slow when including many large files, as 8 often happens when one's binary includes large compiled dependencies (eg. numpy...). Hence 9 we have a fairly elaborate optimisation whereby each python_library rule builds a little 10 zipfile containing just its sources, and all of those are combined at the end to produce 11 the final .pex. This builds at roughly the same pace for a clean build of a single target, 12 but is drastically faster for building many targets with similar dependencies or rebuilding 13 a target which has only had small changes. 14 """ 15 16 17 def python_library(name:str, srcs:list=[], resources:list=[], deps:list=[], visibility:list=None, 18 test_only:bool&testonly=False, zip_safe:bool=True, labels:list&features&tags=[], interpreter:str=None, 19 strip:bool=False): 20 """Generates a Python library target, which collects Python files for use by dependent rules. 21 22 Note that each python_library performs some pre-zipping of its inputs before they're combined 23 in a python_binary or python_test. Hence while it's of course not required that all dependencies 24 of those rules are python_library rules, it's often a good idea to wrap any large dependencies 25 in one to improve incrementality (not necessary for pip_library, of course). 26 27 Args: 28 name (str): Name of the rule. 29 srcs (list): Python source files for this rule. 30 resources (list): Non-Python files that this rule collects which will be included in the final .pex. 31 The distinction between this and srcs is fairly arbitrary and historical, but 32 semantically quite nice and parallels python_test. 33 deps (list): Dependencies of this rule. 34 visibility (list): Visibility specification. 35 test_only (bool): If True, can only be depended on by tests. 36 zip_safe (bool): Should be set to False if this library can't be safely run inside a .pex 37 (the most obvious reason not is when it contains .so modules). 38 See python_binary for more information. 39 labels (list): Labels to apply to this rule. 40 interpreter (str): The Python interpreter to use. Defaults to the config setting 41 which is normally just 'python', but could be 'python3' or 42 'pypy' or whatever. 43 strip (bool): If True, the original sources are stripped and only bytecode is output. 44 """ 45 if not zip_safe: 46 labels.append('py:zip-unsafe') 47 if srcs or resources: 48 cmd = '$TOOLS_JARCAT z -d -o ${OUTS} -i .' 49 interpreter = interpreter or CONFIG.DEFAULT_PYTHON_INTERPRETER 50 if srcs: 51 # This is a bit of a hack, but rather annoying. We want to put bytecode in its 'legacy' location 52 # in python3 because zipimport doesn't look in __pycache__. Unfortunately the flag doesn't exist 53 # in python2 so we have to guess whether we should apply it or not. 54 bytecode_flag = '-b' if 'python3' in interpreter or 'pypy3' in interpreter else '' 55 compile_cmd = '$TOOLS_INT -S -m compileall %s -f $SRCS_SRCS' % bytecode_flag 56 if strip: 57 cmd = ' && '.join([compile_cmd, 'rm -f $SRCS_SRCS', cmd]) 58 else: 59 cmd = ' && '.join([compile_cmd, cmd]) 60 # Pre-zip the files for later collection by python_binary. 61 zip_rule = build_rule( 62 name=name, 63 tag='zip', 64 srcs={ 65 'SRCS': srcs, 66 'RES': resources, 67 }, 68 outs=['.%s.pex.zip' % name], 69 cmd=cmd, 70 building_description='Compressing...', 71 requires=['py'], 72 test_only=test_only, 73 output_is_complete=True, 74 tools={ 75 'int': [interpreter], 76 'jarcat': [CONFIG.JARCAT_TOOL], 77 }, 78 ) 79 deps.append(zip_rule) 80 elif strip: 81 raise ParseError("Can't pass strip=True to a python_library with no srcs") 82 83 return filegroup( 84 name=name, 85 srcs=resources if strip else (srcs + resources), 86 deps=deps, 87 visibility=visibility, 88 output_is_complete=False, 89 requires=['py'], 90 test_only=test_only, 91 labels=labels, 92 ) 93 94 95 def python_binary(name:str, main:str, resources:list=[], out:str=None, deps:list=[], 96 visibility:list=None, zip_safe:bool=None, interpreter:str=None, shebang:str='', 97 labels:list&features&tags=[]): 98 """Generates a Python binary target. 99 100 This compiles all source files together into a single .pex file which can 101 be easily copied or deployed. The construction of the .pex is done in parts 102 by the dependent python_library rules, and this rule simply builds the 103 metadata for it and concatenates them all together. 104 105 Args: 106 name (str): Name of the rule. 107 main (str): Python file which is the entry point and __main__ module. 108 resources (list): List of static resources to include in the .pex. 109 out (str): Name of the output file. Default to name + .pex 110 deps (list): Dependencies of this rule. 111 visibility (list): Visibility specification. 112 zip_safe (bool): Allows overriding whether the output is marked zip safe or not. 113 If set to explicitly True or False, the output will be marked 114 appropriately; by default it will be safe unless any of the 115 transitive dependencies are themselves marked as not zip-safe. 116 interpreter (str): The Python interpreter to use. Defaults to the config setting 117 which is normally just 'python', but could be 'python3' or 118 'pypy' or whatever. 119 shebang (str): Exact shebang to apply to the generated file. By default we will 120 determine something appropriate for the given interpreter. 121 labels (list): Labels to apply to this rule. 122 """ 123 shebang = shebang or interpreter or CONFIG.DEFAULT_PYTHON_INTERPRETER 124 cmd = '$TOOLS_PEX -s "%s" -m "%s" --zip_safe' % (shebang, CONFIG.PYTHON_MODULE_DIR) 125 pre_build, cmd = _handle_zip_safe(cmd, zip_safe) 126 127 lib_rule = python_library( 128 name='_%s#lib' % name, 129 srcs=[main], 130 resources=resources, 131 interpreter=interpreter, 132 deps=deps, 133 visibility=visibility, 134 ) 135 136 # Use the pex tool to compress the entry point & add all the bootstrap helpers etc. 137 pex_rule = build_rule( 138 name = name, 139 tag = 'pex', 140 srcs=[main], 141 outs=['.%s_main.pex.zip' % name], # just call it .zip so everything has the same extension 142 cmd=cmd, 143 requires=['py', 'pex'], 144 pre_build=pre_build, 145 deps=deps, 146 needs_transitive_deps=True, # Needed so we can find anything with zip_safe=False on it. 147 output_is_complete=True, 148 tools={ 149 'interpreter': [interpreter or CONFIG.DEFAULT_PYTHON_INTERPRETER], 150 'pex': [CONFIG.PEX_TOOL], 151 }, 152 ) 153 # This rule concatenates the .pex with all the other precompiled zip files from dependent rules. 154 build_rule( 155 name=name, 156 srcs=[pex_rule], 157 deps=[lib_rule], 158 outs=[out or (name + '.pex')], 159 cmd=_PYTHON_BINARY_CMDS, 160 needs_transitive_deps=True, 161 binary=True, 162 output_is_complete=True, 163 building_description="Creating pex...", 164 visibility=visibility, 165 requires=['py', interpreter or CONFIG.DEFAULT_PYTHON_INTERPRETER], 166 tools=[CONFIG.JARCAT_TOOL], 167 # This makes the python_library rule the dependency for other python_library or 168 # python_test rules that try to import it. Does mean that they cannot collect a .pex 169 # by depending directly on the rule, they'll just get the Python files instead. 170 # This is not a common case anyway; more usually you'd treat that as a runtime data 171 # file rather than trying to pack into a pex. Can be worked around with an 172 # intermediary filegroup rule if really needed. 173 provides={'py': lib_rule}, 174 labels=labels, 175 ) 176 177 178 def python_test(name:str, srcs:list, data:list=[], resources:list=[], deps:list=[], 179 labels:list&features&tags=None, size:str=None, flags:str='', visibility:list=None, 180 container:bool|dict=False, sandbox:bool=None, timeout:int=0, flaky:bool|int=0, 181 test_outputs:list=None, zip_safe:bool=None, interpreter:str=None): 182 """Generates a Python test target. 183 184 This works very similarly to python_binary; it is also a single .pex file 185 which is run to execute the tests. The tests are run via either unittest or pytest, depending 186 on which is set for the test runner, which can be configured either via the python_test_runner 187 package property or python.testrunner in the config. 188 189 Args: 190 name (str): Name of the rule. 191 srcs (list): Source files for this test. 192 data (list): Runtime data files for the test. 193 resources (list): Non-Python files to be included in the pex. Note that the distinction 194 vs. srcs is important here; srcs are passed to unittest for it to run 195 and it may or may not be happy if given non-Python files. 196 deps (list): Dependencies of this rule. 197 labels (list): Labels for this rule. 198 size (str): Test size (enormous, large, medium or small). 199 flags (str): Flags to apply to the test command. 200 visibility (list): Visibility specification. 201 container (bool | dict): If True, the test will be run in a container (eg. Docker). 202 sandbox (bool): Sandbox the test on Linux to restrict access to namespaces such as network. 203 timeout (int): Maximum time this test is allowed to run for, in seconds. 204 flaky (int | bool): True to mark this test as flaky, or an integer for a number of reruns. 205 test_outputs (list): Extra test output files to generate from this test. 206 zip_safe (bool): Allows overriding whether the output is marked zip safe or not. 207 If set to explicitly True or False, the output will be marked 208 appropriately; by default it will be safe unless any of the 209 transitive dependencies are themselves marked as not zip-safe. 210 interpreter (str): The Python interpreter to use. Defaults to the config setting 211 which is normally just 'python', but could be 'python3' or 212 'pypy' or whatever. 213 """ 214 timeout, labels = _test_size_and_timeout(size, timeout, labels) 215 interpreter = interpreter or CONFIG.DEFAULT_PYTHON_INTERPRETER 216 cmd = '$TOOLS_PEX -t -s "%s" -m "%s" -r "%s" --zip_safe' % (interpreter, CONFIG.PYTHON_MODULE_DIR, CONFIG.PYTHON_TEST_RUNNER) 217 pre_build, cmd = _handle_zip_safe(cmd, zip_safe) 218 219 # Use the pex tool to compress the entry point & add all the bootstrap helpers etc. 220 pex_rule = build_rule( 221 name = name, 222 tag = 'pex', 223 srcs=srcs, 224 outs=['.%s_main.pex.zip' % name], # just call it .zip so everything has the same extension 225 cmd=cmd, 226 requires=['py'], 227 test_only=True, 228 needs_transitive_deps=True, # needed for zip-safe detection 229 building_description="Creating pex info...", 230 pre_build=pre_build, 231 deps=deps, 232 tools={ 233 'interpreter': [interpreter or CONFIG.DEFAULT_PYTHON_INTERPRETER], 234 'pex': [CONFIG.PEX_TOOL], 235 }, 236 ) 237 238 # If there are resources specified, they have to get built into the pex. 239 deps = [pex_rule] 240 if resources: 241 # Test library itself. 242 lib_rule = python_library( 243 name='_%s#lib' % name, 244 resources=resources, 245 interpreter=interpreter, 246 deps=deps, 247 test_only=True, 248 ) 249 deps = [pex_rule, lib_rule] 250 251 # This rule concatenates the .pex with all the other precompiled zip files from dependent rules. 252 build_rule( 253 name=name, 254 srcs=[pex_rule], 255 deps=deps, 256 # N.B. the actual test sources are passed as data files as well. This is needed for pytest but 257 # is faster for unittest as well (because we don't need to rebuild the pex if they change). 258 data=data + srcs, 259 outs=['%s.pex' % name], 260 labels=labels, 261 cmd=_PYTHON_BINARY_CMDS, 262 test_cmd = '$TEST ' + flags, 263 needs_transitive_deps=True, 264 output_is_complete=True, 265 binary=True, 266 test=True, 267 container=container, 268 test_sandbox=sandbox, 269 building_description="Building pex...", 270 visibility=visibility, 271 test_timeout=timeout, 272 flaky=flaky, 273 test_outputs=test_outputs, 274 requires=['py', interpreter or CONFIG.DEFAULT_PYTHON_INTERPRETER], 275 tools=[CONFIG.JARCAT_TOOL], 276 ) 277 278 279 def pip_library(name:str, version:str, hashes:list=None, package_name:str=None, outs:list=None, 280 test_only:bool&testonly=False, env:dict=None, deps:list=[], post_install_commands:list=None, 281 install_subdirectory:bool=False, repo:str=None, use_pypi:bool=None, patch:str|list=None, 282 visibility:list=None, zip_safe:bool=True, licences:list=None, pip_flags:str=None): 283 """Provides a build rule for third-party dependencies to be installed by pip. 284 285 Args: 286 name (str): Name of the build rule. 287 version (str): Specific version of the package to install. 288 hashes (list): List of acceptable hashes for this target. 289 package_name (str): Name of the pip package to install. Defaults to the same as 'name'. 290 outs (list): List of output files / directories. Defaults to [name]. 291 test_only (bool): If True, can only be used by test rules or other test_only libraries. 292 env (dict): Deprecated, has no effect. 293 deps (list): List of rules this library depends on. 294 post_install_commands (list): Commands run after pip install has completed. 295 install_subdirectory (bool): Forces the package to install into a subdirectory with this name. 296 repo (str): Allows specifying a custom repo to fetch from. 297 use_pypi (bool): If True, will check PyPI as well for packages. 298 patch (str | list): A patch file or files to be applied after install. 299 visibility (list): Visibility declaration for this rule. 300 zip_safe (bool): Flag to indicate whether a pex including this rule will be zip-safe. 301 licences (list): Licences this rule is subject to. Default attempts to detect from package metadata. 302 pip_flags (str): List of additional flags to pass to pip. 303 """ 304 package_name = '%s==%s' % (package_name or name, version) 305 outs = outs or [name] 306 post_install_commands = post_install_commands or [] 307 post_build = None 308 use_pypi = CONFIG.USE_PYPI if use_pypi is None else use_pypi 309 index_flag = '' if use_pypi else '--no-index' 310 pip_flags = pip_flags or CONFIG.PIP_FLAGS 311 312 repo_flag = '' 313 repo = repo or CONFIG.PYTHON_DEFAULT_PIP_REPO 314 if repo: 315 if repo.startswith('//') or repo.startswith(':'): # Looks like a build label, not a URL. 316 repo_flag = '-f %(location %s)' % repo 317 deps.append(repo) 318 else: 319 repo_flag = '-f ' + repo 320 321 target = outs[0] if install_subdirectory else '.' 322 323 cmd = '$TOOLS_PIP install --no-deps --no-compile --no-cache-dir --default-timeout=60 --target=' + target 324 if CONFIG.OS == 'linux': 325 # Fix for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=830892 326 # tl;dr: Debian has broken --target with a custom patch, the only way to fix is to pass --system 327 # which is itself Debian-specific, so we need to find if we're running on Debian. AAAAARGGGHHHH... 328 cmd = '[ -f /etc/debian_version ] && [ $TOOLS_PIP == "/usr/bin/pip3" ] && SYS_FLAG="--system" || SYS_FLAG=""; ' + cmd + ' $SYS_FLAG' 329 elif CONFIG.OS == 'darwin': 330 # Fix for Homebrew which fails with a superficially similar issue. 331 # https://github.com/Homebrew/brew/blob/master/docs/Homebrew-and-Python.md suggests fixing with --install-option 332 # but that prevents downloading binary wheels. This is more fiddly but seems to work. 333 # Unfortunately it does *not* work similarly on the Debian problem :( 334 cmd = 'echo "[install]\nprefix=" > setup.cfg; ' + cmd 335 cmd += ' -q -b build %s %s %s %s' % (repo_flag, index_flag, pip_flags, package_name) 336 cmd += ' && find . -name "*.pyc" -or -name "tests" | xargs rm -rf' 337 338 if not licences: 339 cmd += ' && find . -name METADATA -or -name PKG-INFO | grep -v "^./build/" | xargs grep -E "License ?:" | grep -v UNKNOWN | cat' 340 341 if install_subdirectory: 342 cmd += ' && touch %s/__init__.py && rm -rf %s/*.egg-info %s/*.dist-info' % (target, target, target) 343 344 if patch: 345 patches = [patch] if isinstance(patch, str) else patch 346 if CONFIG.OS == 'freebsd': 347 # --no-backup-if-mismatch is not supported, but we need to get rid of the .orig 348 # files for hashes to match correctly. 349 cmd += ' && ' + ' && '.join(['patch -p0 < $(location %s)' % patch for patch in patches]) 350 cmd += ' && find . -name "*.orig" | xargs rm' 351 else: 352 cmd += ' && ' + ' && '.join(['patch -p0 --no-backup-if-mismatch < $(location %s)' % patch for patch in patches]) 353 354 if post_install_commands: 355 cmd = ' && '.join([cmd] + post_install_commands) 356 357 # TODO(peterebden): --prefix preserves the old behaviour here, but I'm not sure how intuitive that is; leaving it 358 # out puts everything at the top level of the pex, which is more normal for Python really. 359 cmd += ' && $TOOLS_JARCAT z -d --prefix $PKG_DIR -i ' + ' -i '.join(outs) 360 361 wheel_rule = build_rule( 362 name = name, 363 tag = 'wheel', 364 cmd = cmd, 365 outs = [name + '.whl'], 366 srcs = patches if patch else [], 367 deps = deps, 368 building_description = 'Fetching...', 369 hashes = hashes, 370 requires = ['py'], 371 test_only = test_only, 372 licences = licences, 373 tools = { 374 'pip': [CONFIG.PIP_TOOL], 375 'jarcat': [CONFIG.JARCAT_TOOL], 376 }, 377 post_build = None if licences else _add_licences, 378 sandbox = False, 379 labels = ['py:zip-unsafe'] if not zip_safe else None, 380 ) 381 return build_rule( 382 name = name, 383 srcs = [wheel_rule], 384 outs = outs, 385 cmd = '$TOOL x $SRCS -s $PKG_DIR', 386 tools = [CONFIG.JARCAT_TOOL], 387 labels = ['py', 'pip:' + package_name], 388 provides = {'py': wheel_rule}, 389 visibility = visibility, 390 test_only = test_only, 391 deps = deps, 392 ) 393 394 395 def python_wheel(name:str, version:str, hashes:list=None, package_name:str=None, outs:list=None, 396 post_install_commands:list=None, patch:str|list=None, licences:list=None, 397 test_only:bool&testonly=False, repo:str=None, zip_safe:bool=True, visibility:list=None, 398 deps:list=[], name_scheme:str=None): 399 """Downloads a Python wheel and extracts it. 400 401 This is a lightweight pip-free alternative to pip_library which supports cross-compiling. 402 Rather than leaning on pip which is difficult to achieve reproducible builds with and 403 support on different platforms, this rule is a simple wrapper around curl and unzip. 404 Unless otherwise specified, the wheels are expected to adhere to common naming schemes, 405 such as: 406 <package_name>-<version>[-<os>-<arch>].whl 407 <package_name>-<version>[-<os>_<arch>].whl 408 <package_name>-<version>.whl 409 410 Args: 411 name (str): Name of the rule. Also doubles as the name of the package if package_name 412 is not set. 413 version (str): Version of the package to install. 414 hashes (list): List of hashes to verify the package against. 415 package_name (str): If given, overrides `name` for the name of the package to look for. 416 outs (list): List of output files. Defaults to a directory named the same as `name`. 417 post_install_commands (list): Commands to run after 'install'. 418 patch (str | list): Patch file to apply after install to fix any upstream code that has 419 done bad things. 420 licences (list): Licences that this rule is subject to. 421 test_only (bool): If True, this library can only be used by tests. 422 repo (str): Repository to download wheels from. 423 zip_safe (bool): Flag to indicate whether a pex including this rule will be zip-safe. 424 visibility (list): Visibility declaration. 425 deps (list): Dependencies of this rule. 426 name_scheme (str): The templatized wheel naming scheme (available template variables 427 are `url_base`, `package_name`, and `version`). 428 """ 429 package_name = package_name or name.replace('-', '_') 430 url_base = repo or CONFIG.PYTHON_WHEEL_REPO 431 if not url_base: 432 raise ParseError('python.wheel_repo is not set in the config, must pass repo explicitly ' 433 'to python_wheel') 434 urls = [] 435 if name_scheme: 436 urls.append(name_scheme.format(url_base=url_base, 437 package_name=package_name, 438 version=version)) 439 elif CONFIG.PYTHON_WHEEL_NAME_SCHEME: 440 urls.append(CONFIG.PYTHON_WHEEL_NAME_SCHEME.format(url_base=url_base, 441 package_name=package_name, 442 version=version)) 443 else: 444 # Populate urls using a reasonable set of possible wheel naming schemes. 445 # Look for an arch-specific wheel first; in some cases there can be both (e.g. protobuf 446 # has optional arch-specific bits) and we prefer the one with the cool stuff. 447 urls.append('{url_base}/{package_name}-{version}-${{OS}}-${{ARCH}}.whl'.format(url_base=url_base, 448 package_name=package_name, 449 version=version)) 450 urls.append('{url_base}/{package_name}-{version}-${{OS}}_${{ARCH}}.whl'.format(url_base=url_base, 451 package_name=package_name, 452 version=version)) 453 urls.append('{url_base}/{package_name}-{version}.whl'.format(url_base=url_base, 454 package_name=package_name, 455 version=version)) 456 457 curl_commands = ' || '.join(['curl -fsSO {url}'.format(url=url) for url in urls]) 458 cmd = [ 459 curl_commands, 460 '$TOOL x *.whl', 461 # Strip any bytecode, it might lead to nondeterminism. Similarly some wheels annoyingly 462 # contain test code that we don't want. 463 'find . -name "*.pyc" -or -name "tests" | xargs rm -rf', 464 ] 465 if not licences: 466 cmd.append('find . -name METADATA -or -name PKG-INFO | grep -v "^./build/" | ' 467 'xargs grep -E "License ?:" | grep -v UNKNOWN | cat') 468 if patch: 469 patches = [patch] if isinstance(patch, str) else patch 470 cmd += ['patch -p0 --no-backup-if-mismatch < $(location %s)' % p for p in patches] 471 if post_install_commands: 472 cmd += post_install_commands 473 cmd += ['$TOOL z -d --prefix $PKG -i ' + ' -i '.join(outs or [name])] 474 475 wheel_rule = build_rule( 476 name = name, 477 tag = 'wheel', 478 cmd = ' && '.join(cmd), 479 outs = [name + '.whl'], 480 srcs = patches if patch else None, 481 building_description = 'Downloading...', 482 requires = ['py'], 483 deps = deps, 484 test_only = test_only, 485 licences = licences, 486 tools = [CONFIG.JARCAT_TOOL], 487 post_build = None if licences else _add_licences, 488 sandbox = False, 489 labels = ['py:zip-unsafe'] if not zip_safe else None, 490 ) 491 cmd = '$TOOL x $SRCS -s $PKG_DIR' 492 if outs: 493 # Hacky solution to handle things being in subdirectories in awkward ways. 494 before, _, after = outs[0].partition('/') 495 if after: 496 cmd = 'rm -rf %s && %s' % (before, cmd) 497 return build_rule( 498 name = name, 499 srcs = [wheel_rule], 500 hashes = hashes, # TODO(peterebden): Move this onto wheel_rule when we're willing to break hash compatibility. 501 outs = outs or [name], 502 tools = [CONFIG.JARCAT_TOOL], 503 cmd = cmd, 504 deps = deps, 505 visibility = visibility, 506 test_only = test_only, 507 labels = ['whl:%s==%s' % (package_name, version)], 508 provides = {'py': wheel_rule}, 509 ) 510 511 512 def _handle_zip_safe(cmd, zip_safe): 513 """Handles the zip safe flag. Returns a tuple of (pre-build function, new command).""" 514 if zip_safe is None: 515 return lambda name: (set_command(name, cmd.replace('--zip_safe', ' --nozip_safe')) 516 if has_label(name, 'py:zip-unsafe') else None), cmd 517 elif zip_safe: 518 return None, cmd 519 else: 520 return None, cmd.replace('--zip_safe', ' --nozip_safe') 521 522 523 def _add_licences(name, output): 524 """Annotates a pip_library rule with detected licences after download.""" 525 for line in output: 526 if line.startswith('License: '): 527 for licence in line[9:].split(' or '): # Some are defined this way (eg. "PSF or ZPL") 528 add_licence(name, licence) 529 return 530 elif line.startswith('Classifier: License'): 531 # Oddly quite a few packages seem to have UNKNOWN for the licence but this Classifier 532 # section still seems to know what they are licenced as. 533 add_licence(name, line.split(' :: ')[-1]) 534 return 535 log.warning('No licence found for %s, should add licences = [...] to the rule', 536 name.lstrip('_').split('#')[0]) 537 538 539 # The commands that we use for python_binary and python_test rules. 540 _PYTHON_BINARY_CMDS = { 541 'opt': '$TOOL z -i . -o $OUTS -s .pex.zip -s .whl --preamble_from="$SRC" --include_other --add_init_py --strict', 542 'stripped': '$TOOL z -i . -o $OUTS -s .pex.zip -s .whl --preamble_from="$SRC" --include_other --add_init_py --strict -e .py -x "*.py"', 543 } 544 545 546 if CONFIG.BAZEL_COMPATIBILITY: 547 py_library = python_library 548 py_binary = python_binary 549 py_test = python_test