@@ -28,7 +28,26 @@ load("@bazel_features//:features.bzl", "bazel_features")
2828
2929visibility ("public" )
3030
31- unavailable_xcode_message = "'bazel fetch --configure'"
31+ xcode_config_message_templates = struct (
32+ local_version_located_but_conflicts_with_remote_version = """\
33+ Xcode version {version} was selected, either because --xcode_version was passed, or \
34+ because xcode-select points to this version locally. This corresponds to \
35+ local Xcode version {local_version}. That build of version {version} is not available \
36+ remotely, but there is a different build of version {remote_version}, which has \
37+ version {remote_version} and aliases {remote_version_aliases}. You probably meant to use this version. \
38+ Please download it *and delete version {local_version}, then run `bazel shutdown` \
39+ to continue using dynamic execution. If you really did intend to use \
40+ local version {local_version}, please specify it fully with --xcode_version={local_version}.""" ,
41+ explicit_version_with_local_and_remote_versions_unavailable = """\
42+ --xcode_version={version} specified, but '{version}' is not an available Xcode version. \
43+ Locally available versions: [{local_versions}]. Remotely available versions: [{remote_versions}]. If \
44+ you believe you have '{version}' installed, try running {command}, and then \
45+ re-run your command.""" ,
46+ explicit_version_unavailable = """\
47+ --xcode_version={version} specified, but '{version}' is not an available Xcode version. \
48+ If you believe you have '{version}' installed, try running "bazel shutdown", and then \
49+ re-run your command.""" ,
50+ )
3251
3352def _xcode_config_impl (ctx ):
3453 # TODO: remove when we test with Bazel 8+
@@ -68,11 +87,14 @@ def _xcode_config_impl(ctx):
6887 read_possibly_native_flag (ctx , "xcode_version" ),
6988 read_possibly_native_flag (ctx , "experimental_prefer_mutual_xcode" ),
7089 local_default_version ,
90+ ctx .attr .explicit_version_with_local_and_remote_versions_unavailable_message ,
91+ ctx .attr .local_version_located_but_conflicts_with_remote_version_message ,
7192 )
7293 else :
7394 xcode_version_properties = _resolve_explicitly_defined_version (
7495 explicit_versions ,
7596 explicit_default_version ,
97+ ctx .attr .explicit_version_unavailable_message ,
7698 read_possibly_native_flag (ctx , "xcode_version" ),
7799 )
78100 availability = "UNKNOWN"
@@ -173,6 +195,44 @@ version. This may not be set if `versions` is set.
173195""" ,
174196 providers = [[AvailableXcodesInfo ]],
175197 ),
198+ # Message templates
199+ "explicit_version_with_local_and_remote_versions_unavailable_message" : attr .string (
200+ default = xcode_config_message_templates .explicit_version_with_local_and_remote_versions_unavailable ,
201+ doc = """\
202+ Template string. Contains the following named fields:
203+
204+ - `version`: the provided Xcode version
205+ - `command`: a command to suggest running to reconfigure your Bazel client
206+ - `local_versions`: a comma-separated list of allowed Xcode versions for local execution defined in the xcode_config
207+ - `remote_versions`: a comma-separated list of allowed Xcode versions for remote execution defined in the xcode_config
208+
209+ Emitted when either `local_versions` or `remote_versions` are set in the xcode_config,
210+ but a provided explicitly-defined Xcode version is unavailable under neither of them.""" ,
211+ ),
212+ "explicit_version_unavailable_message" : attr .string (
213+ default = xcode_config_message_templates .explicit_version_unavailable ,
214+ doc = """\
215+ Template string. Contains the following named fields:
216+
217+ - `version`: the provided Xcode version
218+
219+ Emitted when a provided explicitly-defined Xcode version is unavailable under the xcode_config.""" ,
220+ ),
221+ "local_version_located_but_conflicts_with_remote_version_message" : attr .string (
222+ default = xcode_config_message_templates .local_version_located_but_conflicts_with_remote_version ,
223+ doc = """\
224+ Template string. Contains the following named fields:
225+
226+ - `version`: the provided Xcode version
227+ - `local_version`: the resolved local Xcode version that differs from the remote version
228+ - `remote_version`: the resolved remote Xcode version that differs from the local version
229+ - `remote_version_aliases`: any configured aliases the remote version is available under
230+
231+ Emitted when a provided explicitly-defined Xcode version is resolved to a local version
232+ that conflicts with the remote_versions configuration.""" ,
233+ ),
234+
235+ # Private settings
176236 "_xcode_version" : attr .label (
177237 default = "@apple_support//xcode:version" ,
178238 ),
@@ -200,6 +260,11 @@ A single target of this rule can be referenced by the `--xcode_version_config`
200260build flag to translate the `--xcode_version` flag into an accepted official
201261Xcode version. This allows selection of an official Xcode version from a number
202262of registered aliases.
263+
264+ Template string messages abide by the semantics of Starlark. A provided string can
265+ contain any of the named values described by the given attribute's docstring,
266+ but cannot contain any fields beyond those described, any positional fields,
267+ or printf-style conversion specifications (%).
203268""" ,
204269 fragments = ["apple" , "cpp" ],
205270 implementation = _xcode_config_impl ,
@@ -220,17 +285,17 @@ def _use_available_xcodes(
220285 return True
221286 return False
222287
223- def _duplicate_alias_error (alias , versions ):
224- labels_containing_alias = []
225- for version in versions :
226- if alias in version .aliases or (version .xcode_version_properties .xcode_version == alias ):
227- labels_containing_alias .append (str (version .label ))
228- return "'{}' is registered to multiple labels ({}) in a single xcode_config rule" .format (
229- alias ,
230- ", " .join (labels_containing_alias ),
231- )
232-
233288def _aliases_to_xcode_version (versions ):
289+ def _duplicate_alias_error (alias , versions ):
290+ labels_containing_alias = []
291+ for version in versions :
292+ if alias in version .aliases or (version .xcode_version_properties .xcode_version == alias ):
293+ labels_containing_alias .append (str (version .label ))
294+ return "'{alias}' is registered to multiple labels ({labels}) in a single xcode_config rule" .format (
295+ alias = alias ,
296+ labels = ", " .join (labels_containing_alias ),
297+ )
298+
234299 version_map = {}
235300 if not versions :
236301 return version_map
@@ -253,7 +318,9 @@ def _resolve_xcode_from_local_and_remote(
253318 remote_versions ,
254319 xcode_version_flag ,
255320 prefer_mutual_xcode ,
256- local_default_version ):
321+ local_default_version ,
322+ explicit_version_with_local_and_remote_versions_unavailable_message ,
323+ local_version_located_but_conflicts_with_remote_version_message ):
257324 local_alias_to_version_map = _aliases_to_xcode_version (local_versions )
258325 remote_alias_to_version_map = _aliases_to_xcode_version (remote_versions )
259326
@@ -274,38 +341,29 @@ def _resolve_xcode_from_local_and_remote(
274341 remote_version_from_flag = remote_alias_to_version_map .get (xcode_version_flag )
275342 local_version_from_flag = local_alias_to_version_map .get (xcode_version_flag )
276343 availability = "BOTH"
344+ unavailable_xcode_message = "'bazel fetch --configure'"
277345
278346 if remote_version_from_flag and local_version_from_flag :
279347 local_version_from_remote_versions = remote_alias_to_version_map .get (local_version_from_flag .xcode_version_properties .xcode_version )
280348 if local_version_from_remote_versions :
281349 return remote_version_from_flag .xcode_version_properties , availability
282350 else :
283351 fail (
284- ("Xcode version {0} was selected, either because --xcode_version was passed, or" +
285- " because xcode-select points to this version locally. This corresponds to" +
286- " local Xcode version {1}. That build of version {0} is not available" +
287- " remotely, but there is a different build of version {2}, which has" +
288- " version {2} and aliases {3}. You probably meant to use this version." +
289- " Please download it *and delete version {1}, then run `bazel shutdown`" +
290- " to continue using dynamic execution. If you really did intend to use" +
291- " local version {1}, please specify it fully with --xcode_version={1}." ).format (
292- xcode_version_flag ,
293- local_version_from_flag .xcode_version_properties .xcode_version ,
294- remote_version_from_flag .xcode_version_properties .xcode_version ,
295- remote_version_from_flag .aliases ,
352+ local_version_located_but_conflicts_with_remote_version_message .format (
353+ version = xcode_version_flag ,
354+ local_version = local_version_from_flag .xcode_version_properties .xcode_version ,
355+ remote_version = remote_version_from_flag .xcode_version_properties .xcode_version ,
356+ remote_version_aliases = remote_version_from_flag .aliases ,
296357 ),
297358 )
298359
299360 elif local_version_from_flag :
300- error = (
301- " --xcode_version={} specified, but it is not available remotely. Actions " +
302- "requiring Xcode will be run locally, which could make your build slower."
303- ).format (
304- xcode_version_flag ,
305- )
306- if (mutually_available_versions ):
307- error += " Consider using one of [{}]." .format (
308- ", " .join ([version for version in mutually_available_versions ]),
361+ error = """\
362+ --xcode_version={version} specified, but it is not available remotely. Actions \
363+ requiring Xcode will be run locally, which could make your build slower.""" .format (version = xcode_version_flag )
364+ if mutually_available_versions :
365+ error += " Consider using one of [{available_versions}]." .format (
366+ available_versions = ", " .join ([version for version in mutually_available_versions ]),
309367 )
310368
311369 # buildifier: disable=print
@@ -314,29 +372,28 @@ def _resolve_xcode_from_local_and_remote(
314372
315373 elif remote_version_from_flag :
316374 # buildifier: disable=print
317- print (("--xcode_version={version} specified, but it is not available locally. " +
318- "Your build will fail if any actions require a local Xcode. " +
319- "If you believe you have '{version}' installed, try running {command}," +
320- "and then re-run your command. Locally available versions: {local_versions}. " )
321- .format (
322- version = xcode_version_flag ,
323- command = unavailable_xcode_message ,
324- local_versions = ", " .join ([version for version in local_alias_to_version_map .keys ()]),
325- ))
375+ print (
376+ """\
377+ --xcode_version={version} specified, but it is not available locally. \
378+ Your build will fail if any actions require a local Xcode. \
379+ If you believe you have '{version}' installed, try running {command}, \
380+ and then re-run your command. Locally available versions: {local_versions}.""" .format (
381+ version = xcode_version_flag ,
382+ command = unavailable_xcode_message ,
383+ local_versions = ", " .join ([version for version in local_alias_to_version_map .keys ()]),
384+ ),
385+ )
326386 availability = "REMOTE"
327387
328388 return remote_version_from_flag .xcode_version_properties , availability
329389
330390 else : # fail if we can't find any version to match
331391 fail (
332- ("--xcode_version={0} specified, but '{0}' is not an available Xcode version." +
333- " Locally available versions: [{2}]. Remotely available versions: [{3}]. If" +
334- " you believe you have '{0}' installed, try running {1}, and then" +
335- " re-run your command." ).format (
336- xcode_version_flag ,
337- unavailable_xcode_message ,
338- ", " .join ([version .xcode_version_properties .xcode_version for version in local_versions ]),
339- ", " .join ([version .xcode_version_properties .xcode_version for version in remote_versions ]),
392+ explicit_version_with_local_and_remote_versions_unavailable_message .format (
393+ version = xcode_version_flag ,
394+ command = unavailable_xcode_message ,
395+ local_versions = ", " .join ([version .xcode_version_properties .xcode_version for version in local_versions ]),
396+ remote_versions = ", " .join ([version .xcode_version_properties .xcode_version for version in remote_versions ]),
340397 ),
341398 )
342399
@@ -348,14 +405,18 @@ def _resolve_xcode_from_local_and_remote(
348405 if not mutually_available_versions :
349406 # buildifier: disable=print
350407 print (
351- ("Using a local Xcode version, '{}', since there are no" +
352- " remotely available Xcodes on this machine. Consider downloading one of the" +
353- " remotely available Xcode versions ({}) in order to get the best build" +
354- " performance." ).format (local_default_version .xcode_version_properties .xcode_version , ", " .join ([version .xcode_version_properties .xcode_version for version in remote_versions ])),
408+ """\
409+ Using a local Xcode version, '{local_version}', since there are no \
410+ remotely available Xcodes on this machine. Consider downloading one of the \
411+ remotely available Xcode versions ({remote_versions}) in order to get the best build \
412+ performance.""" .format (
413+ local_version = local_default_version .xcode_version_properties .xcode_version ,
414+ remote_versions = ", " .join ([version .xcode_version_properties .xcode_version for version in remote_versions ]),
415+ ),
355416 )
356417 local_version = local_default_version
357418 availability = "LOCAL"
358- elif ( local_default_version .xcode_version_properties .xcode_version in remote_alias_to_version_map ) :
419+ elif local_default_version .xcode_version_properties .xcode_version in remote_alias_to_version_map :
359420 # If the local default version is also available remotely, use it.
360421 availability = "BOTH"
361422 local_version = remote_alias_to_version_map .get (local_default_version .xcode_version_properties .xcode_version )
@@ -396,15 +457,14 @@ def _sdk_version_flag(apple_fragment, name):
396457def _resolve_explicitly_defined_version (
397458 explicit_versions ,
398459 explicit_default_version ,
460+ explicit_version_unavailable_message ,
399461 xcode_version_flag ):
400462 if explicit_default_version and explicit_default_version .label not in [
401463 version .label
402464 for version in explicit_versions
403465 ]:
404466 fail (
405- "default label '{}' must be contained in versions attribute" .format (
406- explicit_default_version .label ,
407- ),
467+ "default label '{label}' must be contained in versions attribute" .format (label = explicit_default_version .label ),
408468 )
409469 if not explicit_versions :
410470 if explicit_default_version :
@@ -417,14 +477,9 @@ def _resolve_explicitly_defined_version(
417477 alias_to_versions = _aliases_to_xcode_version (explicit_versions )
418478 if xcode_version_flag :
419479 flag_version = alias_to_versions .get (str (xcode_version_flag ))
420- if flag_version :
421- return flag_version .xcode_version_properties
422- else :
423- fail (
424- ("--xcode_version={0} specified, but '{0}' is not an available Xcode version. " +
425- "If you believe you have '{0}' installed, try running \" bazel shutdown\" , and then " +
426- "re-run your command." ).format (xcode_version_flag ),
427- )
480+ if not flag_version :
481+ fail (explicit_version_unavailable_message .format (version = xcode_version_flag ))
482+ return flag_version .xcode_version_properties
428483 return alias_to_versions .get (explicit_default_version .xcode_version_properties .xcode_version ).xcode_version_properties
429484
430485def _dotted_version_or_default (field , default ):
0 commit comments