@@ -229,7 +229,14 @@ class Build
229229
230230 handle_native_lisp ( app )
231231
232- IconEmbedder . new ( app , options [ :icon_uri ] ) . embed if options [ :icon_uri ]
232+ if options [ :icon_uri ] || options [ :tahoe_icon_uri ] || options [ :tahoe_icon_name ]
233+ IconEmbedder . new (
234+ app ,
235+ icon_uri : options [ :icon_uri ] ,
236+ tahoe_icon_uri : options [ :tahoe_icon_uri ] ,
237+ tahoe_icon_name : options [ :tahoe_icon_name ]
238+ ) . embed
239+ end
233240 CLIHelperEmbedder . new ( app ) . embed
234241 CSourcesEmbedder . new ( app , @source_dir ) . embed
235242 LibEmbedder . new (
@@ -1417,53 +1424,92 @@ end
14171424class IconEmbedder < AbstractEmbedder
14181425 include Helpers
14191426
1420- def initialize ( app , icon_uri )
1427+ def initialize ( app , icon_uri : nil , tahoe_icon_uri : nil , tahoe_icon_name : nil )
14211428 super ( app )
1422-
14231429 @icon_uri = icon_uri
1430+ @tahoe_icon_uri = tahoe_icon_uri
1431+ @tahoe_icon_name = tahoe_icon_name
14241432 end
14251433
14261434 def embed
1427- return if @icon_uri . nil? || @icon_uri . strip . empty?
1435+ handle_icns if present? ( @icon_uri )
1436+ handle_tahoe if present? ( @tahoe_icon_uri )
1437+ ensure
1438+ cleanup_tmpdir
1439+ end
14281440
1429- source = resolve_source ( @icon_uri )
1441+ private
14301442
1431- unless File . extname ( source ) . downcase == '.icns'
1432- fatal 'Icon must be a .icns file'
1433- end
1443+ def present? ( val )
1444+ ! val . nil? && ! val . strip . empty?
1445+ end
14341446
1447+ def handle_icns
1448+ source = resolve_source ( @icon_uri , '.icns' , 'icon.icns' )
14351449 target = File . join ( resources_dir , 'Emacs.icns' )
1436- info 'Replacing application icon...'
1450+ info 'Replacing application icon (Emacs.icns) ...'
14371451 run_cmd ( 'cp' , '-pRL' , source , target )
1438- ensure
1439- cleanup_download_tmpdir ( source )
1452+ source
14401453 end
14411454
1442- private
1455+ def handle_tahoe
1456+ fatal '--tahoe-icon-name is required with --tahoe-icon-uri' \
1457+ unless present? ( @tahoe_icon_name )
14431458
1444- def resolve_source ( uri )
1445- if valid_url? ( uri )
1446- download_icon ( uri )
1447- else
1448- path = File . expand_path ( uri )
1449- fatal "Icon file does not exist: #{ path } " unless File . exist? ( path )
1450- path
1451- end
1459+ source = resolve_source ( @tahoe_icon_uri , '.car' , 'Assets.car' )
1460+ target = File . join ( resources_dir , 'Assets.car' )
1461+ info 'Placing Tahoe Assets.car into Resources...'
1462+ run_cmd ( 'cp' , '-pRL' , source , target )
1463+
1464+ set_cf_bundle_icon_name ( @tahoe_icon_name )
1465+ source
1466+ end
1467+
1468+ def set_cf_bundle_icon_name ( name )
1469+ info 'Setting CFBundleIconName in Info.plist...'
1470+ info_plist = File . join ( app , 'Contents' , 'Info.plist' )
1471+ fatal "Info.plist not found: #{ info_plist } " unless File . exist? ( info_plist )
1472+
1473+ # Use plutil which adds/replaces the key as needed
1474+ run_cmd (
1475+ 'plutil' , '-replace' , 'CFBundleIconName' , '-string' ,
1476+ name , info_plist
1477+ )
1478+ end
1479+
1480+ def resolve_source ( uri , expected_ext , download_name )
1481+ file_path = if valid_url? ( uri )
1482+ download_file ( uri , download_name )
1483+ else
1484+ local = File . expand_path ( uri )
1485+ unless File . exist? ( local )
1486+ fatal "File does not exist: #{ local } "
1487+ end
1488+ local
1489+ end
1490+
1491+ ext = File . extname ( file_path ) . downcase
1492+ fatal "Unexpected file type: #{ ext } (expected #{ expected_ext } )" \
1493+ unless ext == expected_ext
1494+
1495+ file_path
14521496 end
14531497
1454- def download_icon ( url )
1455- @download_tmpdir = Dir . mktmpdir ( %w[ emacs-icon .tmp ] )
1456- path = File . join ( @download_tmpdir , 'icon.icns' )
1457- info "Downloading icon from: #{ url } "
1498+ def tmpdir
1499+ @tmpdir ||= Dir . mktmpdir ( %w[ emacs-assets .tmp ] )
1500+ end
1501+
1502+ def download_file ( url , name )
1503+ path = File . join ( tmpdir , name )
1504+ info "Downloading asset from: #{ url } "
14581505 run_cmd ( 'curl' , '-L#' , url , '-o' , path )
14591506 path
14601507 end
14611508
1462- def cleanup_download_tmpdir ( source )
1463- return unless @download_tmpdir && source
1464- return unless source . start_with? ( @download_tmpdir )
1509+ def cleanup_tmpdir
1510+ return unless @tmpdir
14651511
1466- FileUtils . rm_rf ( @download_tmpdir )
1512+ FileUtils . rm_rf ( @tmpdir )
14671513 end
14681514end
14691515
@@ -2155,6 +2201,14 @@ class CLIOptions
21552201
21562202 def parse! ( args )
21572203 parser . parse! ( args )
2204+
2205+ if options [ :tahoe_icon_uri ] &&
2206+ (
2207+ options [ :tahoe_icon_name ] . nil? ||
2208+ options [ :tahoe_icon_name ] . strip . empty?
2209+ )
2210+ fatal '--tahoe-icon-name is required when --tahoe-icon-uri is specified'
2211+ end
21582212 rescue OptionParser ::InvalidOption => e
21592213 fatal e . message
21602214 end
@@ -2182,6 +2236,8 @@ class CLIOptions
21822236 github_auth : true ,
21832237 dist_include : [ 'COPYING' , 'configure_output.txt' ] ,
21842238 icon_uri : nil ,
2239+ tahoe_icon_uri : nil ,
2240+ tahoe_icon_name : nil ,
21852241 self_sign : true ,
21862242 archive : true ,
21872243 archive_keep : false ,
@@ -2383,6 +2439,17 @@ class CLIOptions
23832439 'Local path or URL to a .icns file to replace the default app icon'
23842440 ) { |v | options [ :icon_uri ] = v }
23852441
2442+ opts . on (
2443+ '--tahoe-icon-uri URI' ,
2444+ 'Local path or URL to an Assets.car file for macOS 26 icons. ' \
2445+ 'Requires --tahoe-icon-name.'
2446+ ) { |v | options [ :tahoe_icon_uri ] = v }
2447+
2448+ opts . on (
2449+ '--tahoe-icon-name NAME' ,
2450+ 'Name of the icon in Assets.car to set as CFBundleIconName'
2451+ ) { |v | options [ :tahoe_icon_name ] = v }
2452+
23862453 opts . on (
23872454 '--[no-]self-sign' ,
23882455 'Enable/disable self-signing of Emacs.app (default: enabled)'
0 commit comments