4949
5050_supports_visionos = hasattr (apple_common .platform_type , "visionos" )
5151
52+ # TODO: b/425967223 - Rework the validation to better account for Xcode 26, and make it a bit more
53+ # readable via helper functions as we add more conditions.
54+
5255def _actool_args_for_special_file_types (
5356 * ,
5457 asset_files ,
@@ -77,6 +80,13 @@ def _actool_args_for_special_file_types(
7780 """
7881 args = []
7982
83+ is_xcode_26_or_later = (
84+ platform_prerequisites .xcode_version_config .xcode_version () >=
85+ apple_common .dotted_version ("26.0" )
86+ )
87+ icon_files = []
88+ icon_bundle_files = []
89+
8090 if product_type in (
8191 apple_product_type .messages_extension ,
8292 apple_product_type .messages_sticker_pack_extension ,
@@ -121,27 +131,72 @@ def _actool_args_for_special_file_types(
121131 appicon_extension = "solidimagestack"
122132 icon_files = [f for f in asset_files if ".solidimagestack/" in f .path ]
123133 else :
134+ icon_bundle_files = [f for f in asset_files if ".icon/" in f .path ]
124135 appicon_extension = "appiconset"
125136 icon_files = [f for f in asset_files if ".appiconset/" in f .path ]
126137
138+ if not is_xcode_26_or_later and len (icon_bundle_files ):
139+ fail ("""\
140+ Found Icon Composer .icon bundles among the assigned app_icons. These are only supported \
141+ on Xcode 26 or later.""" )
142+
127143 # Add arguments for app icons, if there are any.
128- if icon_files :
144+ if icon_files or icon_bundle_files :
129145 icon_dirs = group_files_by_directory (
130146 icon_files ,
131147 [appicon_extension ],
132148 attr = "app_icons" ,
133149 ).keys ()
134- if len (icon_dirs ) != 1 and not primary_icon_name :
150+ has_exactly_one_icon_dir = False
151+
152+ if is_xcode_26_or_later :
153+ icon_bundle_dirs = group_files_by_directory (
154+ icon_bundle_files ,
155+ ["icon" ],
156+ attr = "app_icons" ,
157+ ).keys ()
158+
159+ if len (icon_dirs + icon_bundle_dirs ) == 1 :
160+ has_exactly_one_icon_dir = True
161+ elif len (icon_dirs ) == 1 and len (icon_bundle_dirs ) == 1 :
162+ # Carve out; the AppIcon and Icon bundles can be used together to support Apple OSes
163+ # prior to 26 and the new Apple OS 26 icon features for iOS/macOS/watchOS as long as
164+ # their names match perfectly.
165+ icon_paths_to_compare = [icon_dirs [0 ], icon_bundle_dirs [0 ]]
166+ unique_icon_names = dict ()
167+ for icon_path in icon_paths_to_compare :
168+ unique_icon_names [paths .split_extension (paths .basename (icon_path ))[0 ]] = True
169+ if len (unique_icon_names ) == 1 :
170+ has_exactly_one_icon_dir = True
171+ icon_dirs .extend (icon_bundle_dirs )
172+
173+ elif len (icon_dirs ) == 1 :
174+ has_exactly_one_icon_dir = True
175+
176+ if not has_exactly_one_icon_dir and not primary_icon_name :
135177 formatted_dirs = "[\n %s\n ]" % ",\n " .join (icon_dirs )
136178
137179 # Alternate icons are only supported for UIKit applications on iOS, tvOS, visionOS and
138180 # iOS-on-macOS (Catalyst)
139181 if (platform_prerequisites .platform_type == apple_common .platform_type .watchos or
140182 platform_prerequisites .platform_type == apple_common .platform_type .macos or
141183 product_type != apple_product_type .application ):
142- fail ("The asset catalogs should contain exactly one directory named " +
143- "*.%s among its asset catalogs, " % appicon_extension +
144- "but found the following: " + formatted_dirs , "app_icons" )
184+ xcode_26_workaround_message = ""
185+ if is_xcode_26_or_later :
186+ xcode_26_workaround_message = (
187+ "which can be accompanied by exactly one Icon Composer .icon bundle of " +
188+ "the same name, "
189+ )
190+ fail ("""
191+ The asset catalogs should contain exactly one directory named *.{appicon_extension} among its \
192+ asset catalogs, \
193+ {xcode_26_workaround_message}\
194+ but found the following: \
195+ {formatted_dirs}""" .format (
196+ appicon_extension = appicon_extension ,
197+ formatted_dirs = formatted_dirs ,
198+ xcode_26_workaround_message = xcode_26_workaround_message ,
199+ ), "app_icons" )
145200 else :
146201 fail ("""
147202Found multiple app icons among the asset catalogs with no primary_app_icon assigned.
@@ -279,13 +334,27 @@ def compile_asset_catalog(
279334 "actool" ,
280335 "--compile" ,
281336 xctoolrunner_support .prefixed_path (output_dir .path ),
337+ "--errors" ,
338+ "--warnings" ,
339+ "--notices" ,
340+ "--output-format" ,
341+ "human-readable-text" ,
282342 "--platform" ,
283343 actool_platform ,
284344 "--minimum-deployment-target" ,
285345 platform_prerequisites .minimum_os ,
286346 "--compress-pngs" ,
287347 ]
288348
349+ xcode_config = platform_prerequisites .xcode_version_config
350+
351+ if platform_prerequisites .platform_type == "macos" and (
352+ xcode_config .xcode_version () >= apple_common .dotted_version ("26.0" )
353+ ):
354+ # Required for the Icon Composer .icon bundles to work as inputs, even though it's not
355+ # documented. Xcode 26 currently relies on this flag to be set.
356+ args .extend (["--lightweight-asset-runtime-mode" , "enabled" ])
357+
289358 args .extend (_actool_args_for_special_file_types (
290359 asset_files = asset_files ,
291360 bundle_id = bundle_id ,
@@ -321,7 +390,7 @@ def compile_asset_catalog(
321390
322391 xcassets = group_files_by_directory (
323392 asset_files ,
324- ["xcassets" , "xcstickers" ],
393+ ["icon" , " xcassets" , "xcstickers" ],
325394 attr = "asset_catalogs" ,
326395 ).keys ()
327396
@@ -336,7 +405,7 @@ def compile_asset_catalog(
336405 inputs = asset_files ,
337406 mnemonic = "AssetCatalogCompile" ,
338407 outputs = actool_outputs ,
339- xcode_config = platform_prerequisites . xcode_version_config ,
408+ xcode_config = xcode_config ,
340409 )
341410
342411 if alternate_icons :
0 commit comments