@@ -206,6 +206,11 @@ accepting arbitrary Python versions.
206206 allow_single_file = True ,
207207 default = "@bazel_tools//tools/python:python_bootstrap_template.txt" ,
208208 ),
209+ "_build_data_writer" : lambda : attrb .Label (
210+ default = "//python/private:build_data_writer" ,
211+ executable = True ,
212+ cfg = "exec" ,
213+ ),
209214 "_debugger_flag" : lambda : attrb .Label (
210215 default = "//python/private:debugger_if_target_config" ,
211216 providers = [PyInfo ],
@@ -226,6 +231,10 @@ accepting arbitrary Python versions.
226231 "_python_version_flag" : lambda : attrb .Label (
227232 default = labels .PYTHON_VERSION ,
228233 ),
234+ "_uncachable_version_file" : lambda : attrb .Label (
235+ default = "//python/private:uncachable_version_file" ,
236+ allow_files = True ,
237+ ),
229238 "_venvs_use_declare_symlink_flag" : lambda : attrb .Label (
230239 default = labels .VENVS_USE_DECLARE_SYMLINK ,
231240 providers = [BuildSettingInfo ],
@@ -271,10 +280,8 @@ def py_executable_impl(ctx, *, is_test, inherited_environment):
271280def create_binary_semantics ():
272281 return create_binary_semantics_struct (
273282 # keep-sorted start
274- get_central_uncachable_version_file = lambda ctx : None ,
275283 get_native_deps_dso_name = _get_native_deps_dso_name ,
276284 should_build_native_deps_dso = lambda ctx : False ,
277- should_include_build_data = lambda ctx : False ,
278285 # keep-sorted end
279286 )
280287
@@ -336,6 +343,7 @@ def _create_executable(
336343 imports = imports ,
337344 runtime_details = runtime_details ,
338345 venv = venv ,
346+ build_data_file = runfiles_details .build_data_file ,
339347 )
340348 extra_runfiles = ctx .runfiles (
341349 [stage2_bootstrap ] + (
@@ -648,6 +656,7 @@ def _create_stage2_bootstrap(
648656 main_py ,
649657 imports ,
650658 runtime_details ,
659+ build_data_file ,
651660 venv ):
652661 output = ctx .actions .declare_file (
653662 # Prepend with underscore to prevent pytest from trying to
@@ -668,6 +677,7 @@ def _create_stage2_bootstrap(
668677 template = template ,
669678 output = output ,
670679 substitutions = {
680+ "%build_data_file%" : runfiles_root_path (ctx , build_data_file .short_path ),
671681 "%coverage_tool%" : _get_coverage_tool_runfiles_path (ctx , runtime ),
672682 "%import_all%" : "True" if read_possibly_native_flag (ctx , "python_import_all_repositories" ) else "False" ,
673683 "%imports%" : ":" .join (imports .to_list ()),
@@ -1241,8 +1251,7 @@ def _get_base_runfiles_for_binary(
12411251 required_pyc_files ,
12421252 implicit_pyc_files ,
12431253 implicit_pyc_source_files ,
1244- extra_common_runfiles ,
1245- semantics ):
1254+ extra_common_runfiles ):
12461255 """Returns the set of runfiles necessary prior to executable creation.
12471256
12481257 NOTE: The term "common runfiles" refers to the runfiles that are common to
@@ -1264,7 +1273,6 @@ def _get_base_runfiles_for_binary(
12641273 files that are used when the implicit pyc files are not.
12651274 extra_common_runfiles: List of runfiles; additional runfiles that
12661275 will be added to the common runfiles.
1267- semantics: A `BinarySemantics` struct; see `create_binary_semantics_struct`.
12681276
12691277 Returns:
12701278 struct with attributes:
@@ -1305,6 +1313,9 @@ def _get_base_runfiles_for_binary(
13051313 common_runfiles .add_targets (extra_deps )
13061314 common_runfiles .add (extra_common_runfiles )
13071315
1316+ build_data_file = _write_build_data (ctx )
1317+ common_runfiles .add (build_data_file )
1318+
13081319 common_runfiles = common_runfiles .build (ctx )
13091320
13101321 if _should_create_init_files (ctx ):
@@ -1313,25 +1324,10 @@ def _get_base_runfiles_for_binary(
13131324 runfiles = common_runfiles ,
13141325 )
13151326
1316- # Don't include build_data.txt in the non-exe runfiles. The build data
1317- # may contain program-specific content (e.g. target name).
13181327 runfiles_with_exe = common_runfiles .merge (ctx .runfiles ([executable ]))
13191328
1320- # Don't include build_data.txt in data runfiles. This allows binaries to
1321- # contain other binaries while still using the same fixed location symlink
1322- # for the build_data.txt file. Really, the fixed location symlink should be
1323- # removed and another way found to locate the underlying build data file.
13241329 data_runfiles = runfiles_with_exe
1325-
1326- if is_stamping_enabled (ctx ) and semantics .should_include_build_data (ctx ):
1327- build_data_file , build_data_runfiles = _create_runfiles_with_build_data (
1328- ctx ,
1329- semantics .get_central_uncachable_version_file (ctx ),
1330- )
1331- default_runfiles = runfiles_with_exe .merge (build_data_runfiles )
1332- else :
1333- build_data_file = None
1334- default_runfiles = runfiles_with_exe
1330+ default_runfiles = runfiles_with_exe
13351331
13361332 return struct (
13371333 runfiles_without_exe = common_runfiles ,
@@ -1340,31 +1336,18 @@ def _get_base_runfiles_for_binary(
13401336 data_runfiles = data_runfiles ,
13411337 )
13421338
1343- def _create_runfiles_with_build_data (
1344- ctx ,
1345- central_uncachable_version_file ):
1346- build_data_file = _write_build_data (
1347- ctx ,
1348- central_uncachable_version_file ,
1349- )
1350- build_data_runfiles = ctx .runfiles (files = [
1351- build_data_file ,
1352- ])
1353- return build_data_file , build_data_runfiles
1354-
1355- def _write_build_data (ctx , central_uncachable_version_file ):
1356- # TODO: Remove this logic when a central file is always available
1357- if not central_uncachable_version_file :
1358- version_file = ctx .actions .declare_file (ctx .label .name + "-uncachable_version_file.txt" )
1359- _py_builtins .copy_without_caching (
1360- ctx = ctx ,
1361- read_from = ctx .version_file ,
1362- write_to = version_file ,
1363- )
1339+ def _write_build_data (ctx ):
1340+ inputs = []
1341+ if is_stamping_enabled (ctx ):
1342+ # NOTE: ctx.info_file is undocumented; see
1343+ # https://github.com/bazelbuild/bazel/issues/9363
1344+ info_file = ctx .info_file
1345+ version_file = ctx .files ._uncachable_version_file [0 ]
1346+ inputs = [info_file , version_file ]
13641347 else :
1365- version_file = central_uncachable_version_file
1366-
1367- direct_inputs = [ ctx . info_file , version_file ]
1348+ inputs = []
1349+ info_file = None
1350+ version_file = None
13681351
13691352 # A "constant metadata" file is basically a special file that doesn't
13701353 # support change detection logic and reports that it is unchanged. i.e., it
@@ -1397,19 +1380,23 @@ def _write_build_data(ctx, central_uncachable_version_file):
13971380 )
13981381
13991382 ctx .actions .run (
1400- executable = ctx .executable ._build_data_gen ,
1383+ executable = ctx .executable ._build_data_writer ,
14011384 env = {
1402- # NOTE: ctx.info_file is undocumented; see
1403- # https://github.com/bazelbuild/bazel/issues/9363
1404- "INFO_FILE" : ctx .info_file .path ,
1385+ # Include config id so binaries can e.g. cache content based on how
1386+ # they were built.
1387+ "CONFIG_ID" : ctx .configuration .short_id ,
1388+ # Include config mode so that binaries can detect if they're
1389+ # being used as a build tool or not, allowing for runtime optimizations.
1390+ "CONFIG_MODE" : "EXEC" if _is_tool_config (ctx ) else "TARGET" ,
1391+ "INFO_FILE" : info_file .path if info_file else "" ,
14051392 "OUTPUT" : build_data .path ,
1406- "PLATFORM" : cc_helper .find_cpp_toolchain (ctx ).toolchain_id ,
1393+ # Include this so it's explicit, otherwise, one has to detect
1394+ # this by looking for the absense of info_file keys.
1395+ "STAMPED" : "TRUE" if is_stamping_enabled (ctx ) else "FALSE" ,
14071396 "TARGET" : str (ctx .label ),
1408- "VERSION_FILE" : version_file .path ,
1397+ "VERSION_FILE" : version_file .path if version_file else "" ,
14091398 },
1410- inputs = depset (
1411- direct = direct_inputs ,
1412- ),
1399+ inputs = depset (direct = inputs ),
14131400 outputs = [build_data ],
14141401 mnemonic = "PyWriteBuildData" ,
14151402 progress_message = "Generating %{label} build_data.txt" ,
@@ -1607,6 +1594,9 @@ def is_stamping_enabled(ctx):
16071594 Returns:
16081595 bool; True if stamping is enabled, False if not.
16091596 """
1597+
1598+ # Always ignore stamping for exec config. This mitigates stamping
1599+ # invalidating build action caching.
16101600 if _is_tool_config (ctx ):
16111601 return False
16121602
@@ -1616,8 +1606,9 @@ def is_stamping_enabled(ctx):
16161606 elif stamp == 0 :
16171607 return False
16181608 elif stamp == - 1 :
1619- # NOTE: Undocumented API; private to builtins
1620- return ctx .configuration .stamp_binaries
1609+ # NOTE: ctx.configuration.stamp_binaries() exposes this, but that's
1610+ # a private API. To workaround, it'd been eposed via py_internal.
1611+ return py_internal .stamp_binaries (ctx )
16211612 else :
16221613 fail ("Unsupported `stamp` value: {}" .format (stamp ))
16231614
@@ -1770,6 +1761,9 @@ def _transition_executable_impl(settings, attr):
17701761
17711762 if attr .python_version and attr .python_version not in ("PY2" , "PY3" ):
17721763 settings [labels .PYTHON_VERSION ] = attr .python_version
1764+
1765+ if attr .stamp != - 1 :
1766+ settings ["//command_line_option:stamp" ] = str (attr .stamp )
17731767 return settings
17741768
17751769def create_executable_rule (* , attrs , ** kwargs ):
@@ -1820,8 +1814,14 @@ def create_executable_rule_builder(implementation, **kwargs):
18201814 ] + ([ruleb .ToolchainType (_LAUNCHER_MAKER_TOOLCHAIN_TYPE )] if rp_config .bazel_9_or_later else []),
18211815 cfg = dict (
18221816 implementation = _transition_executable_impl ,
1823- inputs = TRANSITION_LABELS + [labels .PYTHON_VERSION ],
1824- outputs = TRANSITION_LABELS + [labels .PYTHON_VERSION ],
1817+ inputs = TRANSITION_LABELS + [
1818+ labels .PYTHON_VERSION ,
1819+ "//command_line_option:stamp" ,
1820+ ],
1821+ outputs = TRANSITION_LABELS + [
1822+ labels .PYTHON_VERSION ,
1823+ "//command_line_option:stamp" ,
1824+ ],
18251825 ),
18261826 ** kwargs
18271827 )
0 commit comments