Building Icons with GNU Make
Desktop icon formats are a mess. No platforms share formats - Windows uses .ico (and for various reasons so does the web), Mac OS X uses .icns, and desktop GNU/Linux doesn't even have an icon format, you just throw PNGs into the air and pray.
But let's say you are foolishly trying to write a cross-platform application, yet intelligently want to automate creating most of these from source data.
A reasonable "source data" for icons is a directory full of
PNGs.1 Apple uses a format called .iconset for this, with filenames
like example.iconset/icon_64x64.png
. Since it's just a directory of
images, it'll serve for making Windows/website icons as well, and
GNU/Linux build scripts can copy files out of it directly.
To convert this to the desired formats, you'll need GraphicsMagick, and either build on a Mac OS X system or install libicns.
Now for the Makefile:
ICONUTIL := $(word 1, $(shell command -v iconutil icnsutil) iconutil)
.SECONDEXPANSION:
%.icns: %.iconset $$(wildcard $$(@D)/$$*.iconset/icon_*.png)
$(ICONUTIL) -c icns -o $@ $<
.SECONDEXPANSION:
%.ico: %.iconset $$(wildcard $$(@D)/$$*.iconset/icon_*[0-9].png)
convert -background transparent -colors 256 $(filter-out $<,$^) $@
(You'll need to replace those spaces with tabs. Sorry. Complain here.)
This uses a couple unusual tricks.
First, the ICONUTIL
assignment figures out whether you've got the
genuine iconutil
from Mac OS X, or the compatible icnsutil
from
libicns. If you don't have either, it picks iconutil
so you get a
sensible error.
Second, it uses GNU Make's secondary expansion feature. This specifies a wildcard dependent on the pattern and target name when generating the list of source names.
Third, it depends on the source directory. This is uncommon in
Makefiles because directories update when contained files are added or
deleted, not when files are modified. The modification case is covered
with the .SECONDEXPANSION
trick but we also need to handle the
case where an icon size was removed, because the target files need to
be rebuilt without the removed file.
Finally, please note the slightly different glob patterns. .icns
files support high-DPI ("Retina") icons using the format
icon_WxH@2.png
- so icon_16x16@2.png
is actually 32 pixels on each
side. .ico
files, as far as I can determine, have no such feature;
they will use the 32x32 icon instead. So @2x
files should not
be included when building the .ico
.
Assigning a .ico to a .exe
I've not found a good cross-platform tool for doing this.
GitHub's Atom project has produced a Windows tool called rcedit (compiled binary here) which does nothing but poke icons and version numbers into Windows executables. Because it is so minimal it runs perfectly in Wine, and it has an MIT-style license.
wine rcedit.exe --set-icon example.ico example.exe
Assigning a .icns to a .app
Mac OS X .app
files are directories. The usual place to put an
icon would be Contents/Resources/Icon.icns
, and you may simply need to
copy it over the file already there.
If you're building the package from scratch, you'll need to create the Info.plist file referencing the icon in the first place. This can be done on any platform with Python's plistlib.
-
Another reasonable source format is one or multiple SVGs. Turning an SVG into a directory of PNGs is left as an exercise for the reader. ↩