@@ -1162,14 +1162,44 @@ def _additional_inputs_configurator(prerequisites, args):
11621162 inputs = prerequisites .additional_inputs ,
11631163 )
11641164
1165+ def _module_name_safe (string ):
1166+ """Returns a transformation of `string` that is safe for module names."""
1167+ result = ""
1168+ saw_non_identifier_char = False
1169+ for ch in string .elems ():
1170+ if ch .isalnum () or ch == "_" :
1171+ # If we're seeing an identifier character after a sequence of
1172+ # non-identifier characters, append an underscore and reset our
1173+ # tracking state before appending the identifier character.
1174+ if saw_non_identifier_char :
1175+ result += "_"
1176+ saw_non_identifier_char = False
1177+ result += ch
1178+ elif result :
1179+ # Only track this if `result` has content; this ensures that we
1180+ # (intentionally) drop leading non-identifier characters instead of
1181+ # adding a leading underscore.
1182+ saw_non_identifier_char = True
1183+
1184+ return result
1185+
11651186def derive_module_name (* args ):
11661187 """Returns a derived module name from the given build label.
11671188
11681189 For targets whose module name is not explicitly specified, the module name
1169- is computed by creating an underscore-delimited string from the components
1170- of the label, replacing any non-identifier characters also with underscores.
1190+ is computed using the following algorithm:
1191+
1192+ * The package and name components of the label are considered separately.
1193+ All _interior_ sequences of non-identifier characters (anything other
1194+ than `a-z`, `A-Z`, `0-9`, and `_`) are replaced by a single underscore
1195+ (`_`). Any leading or trailing non-identifier characters are dropped.
1196+ * If the package component is non-empty after the above transformation,
1197+ it is joined with the transformed name component using an underscore.
1198+ Otherwise, the transformed name is used by itself.
1199+ * If this would result in a string that begins with a digit (`0-9`), an
1200+ underscore is prepended to make it identifier-safe.
11711201
1172- This mapping is not intended to be reversible.
1202+ This mapping is intended to be fairly predictable, but not reversible.
11731203
11741204 Args:
11751205 *args: Either a single argument of type `Label`, or two arguments of
@@ -1194,12 +1224,15 @@ def derive_module_name(*args):
11941224 fail ("derive_module_name may only be called with a single argument " +
11951225 "of type 'Label' or two arguments of type 'str'." )
11961226
1197- package_part = (package .lstrip ("//" ).replace ("/" , "_" ).replace ("-" , "_" )
1198- .replace ("." , "_" ))
1199- name_part = name .replace ("-" , "_" )
1227+ package_part = _module_name_safe (package .lstrip ("//" ))
1228+ name_part = _module_name_safe (name )
12001229 if package_part :
1201- return package_part + "_" + name_part
1202- return name_part
1230+ module_name = package_part + "_" + name_part
1231+ else :
1232+ module_name = name_part
1233+ if module_name [0 ].isdigit ():
1234+ module_name = "_" + module_name
1235+ return module_name
12031236
12041237def compile (
12051238 * ,
0 commit comments