Bug 786520 - Install things to $(DIST)/branding from moz.build instead of manual rules in Makefile.ins. r=mshal
authorBrian O'Keefe <bokeefe@alum.wpi.edu>
Thu, 26 Feb 2015 16:53:31 -0500
changeset 242081 be97e101c20cd46c3800219536be663f95b0ecc0
parent 242080 4e1ffa5e0202fa10ede528e16cdaf683adf2982f
child 242082 b242594427fd4d353440287b7fdb00f4b847775a
push id12704
push userphilringnalda@gmail.com
push dateSat, 02 May 2015 17:20:25 +0000
treeherderfx-team@acf6ede9d678 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmshal
bugs786520
milestone40.0a1
Bug 786520 - Install things to $(DIST)/branding from moz.build instead of manual rules in Makefile.ins. r=mshal
Makefile.in
browser/branding/aurora/Makefile.in
browser/branding/aurora/moz.build
browser/branding/branding-common.mozbuild
browser/branding/nightly/Makefile.in
browser/branding/nightly/moz.build
browser/branding/official/Makefile.in
browser/branding/official/moz.build
browser/branding/unofficial/Makefile.in
browser/branding/unofficial/moz.build
python/mozbuild/mozbuild/backend/recursivemake.py
python/mozbuild/mozbuild/frontend/context.py
python/mozbuild/mozbuild/frontend/data.py
python/mozbuild/mozbuild/frontend/emitter.py
python/mozbuild/mozbuild/test/backend/data/branding-files/bar.ico
python/mozbuild/mozbuild/test/backend/data/branding-files/foo.ico
python/mozbuild/mozbuild/test/backend/data/branding-files/moz.build
python/mozbuild/mozbuild/test/backend/data/branding-files/sub/quux.png
python/mozbuild/mozbuild/test/backend/test_recursivemake.py
python/mozbuild/mozbuild/test/frontend/data/branding-files/moz.build
python/mozbuild/mozbuild/test/frontend/test_emitter.py
xulrunner/app/Makefile.in
xulrunner/app/moz.build
--- a/Makefile.in
+++ b/Makefile.in
@@ -90,17 +90,17 @@ backend.RecursiveMakeBackend:
 Makefile: backend.RecursiveMakeBackend
 	@$(TOUCH) $@
 
 include backend.RecursiveMakeBackend.pp
 
 default:: backend.RecursiveMakeBackend
 
 install_manifests := \
-  $(addprefix dist/,bin idl include public private sdk xpi-stage) \
+  $(addprefix dist/,bin branding idl include public private sdk xpi-stage) \
   _tests \
   $(NULL)
 install_manifest_depends = \
   CLOBBER \
   $(configure_dir)/configure \
   config.status \
   backend.RecursiveMakeBackend \
   $(NULL)
deleted file mode 100644
--- a/browser/branding/aurora/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-include $(topsrcdir)/config/config.mk
-
-ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
-BRANDING_FILES := \
-	firefox.ico \
-	document.ico \
-	branding.nsi \
-	appname.bmp \
-	bgintro.bmp \
-	clock.bmp \
-	particles.bmp \
-	pencil.bmp \
-	pencil-rtl.bmp \
-	wizHeader.bmp \
-	wizHeaderRTL.bmp \
-	wizWatermark.bmp \
-	newwindow.ico \
-	newtab.ico \
-	pbmode.ico \
-	$(NULL)
-endif
-
-ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
-BRANDING_FILES := \
-	background.png \
-	firefox.icns \
-	disk.icns \
-	document.icns \
-	dsstore \
-	$(NULL)
-endif
-
-ifdef MOZ_WIDGET_GTK
-BRANDING_FILES := \
-	default16.png \
-	default32.png \
-	default48.png \
-	mozicon128.png \
-	$(NULL)
-endif
-
-BRANDING_DEST := $(DIST)/branding
-BRANDING_TARGET := export
-INSTALL_TARGETS += BRANDING
--- a/browser/branding/aurora/moz.build
+++ b/browser/branding/aurora/moz.build
@@ -4,12 +4,9 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += ['content', 'locales']
 
 DIST_SUBDIR = 'browser'
 export('DIST_SUBDIR')
 
-JS_PREFERENCE_FILES += [
-    'pref/firefox-branding.js',
-]
-
+include('../branding-common.mozbuild')
new file mode 100644
--- /dev/null
+++ b/browser/branding/branding-common.mozbuild
@@ -0,0 +1,43 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JS_PREFERENCE_FILES += [
+    'pref/firefox-branding.js',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+    BRANDING_FILES += [
+        'appname.bmp',
+        'bgintro.bmp',
+        'branding.nsi',
+        'clock.bmp',
+        'document.ico',
+        'firefox.ico',
+        'newtab.ico',
+        'newwindow.ico',
+        'particles.bmp',
+        'pbmode.ico',
+        'pencil-rtl.bmp',
+        'pencil.bmp',
+        'wizHeader.bmp',
+        'wizHeaderRTL.bmp',
+        'wizWatermark.bmp',
+     ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+    BRANDING_FILES += [
+        'background.png',
+        'disk.icns',
+        'document.icns',
+        'dsstore',
+        'firefox.icns',
+    ]
+elif CONFIG['MOZ_WIDGET_GTK']:
+    BRANDING_FILES += [
+        'default16.png',
+        'default32.png',
+        'default48.png',
+        'mozicon128.png',
+    ]
deleted file mode 100644
--- a/browser/branding/nightly/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-include $(topsrcdir)/config/config.mk
-
-ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
-BRANDING_FILES := \
-	firefox.ico \
-	document.ico \
-	branding.nsi \
-	appname.bmp \
-	bgintro.bmp \
-	clock.bmp \
-	particles.bmp \
-	pencil.bmp \
-	pencil-rtl.bmp \
-	wizHeader.bmp \
-	wizHeaderRTL.bmp \
-	wizWatermark.bmp \
-	newwindow.ico \
-	newtab.ico \
-	pbmode.ico \
-	$(NULL)
-endif
-
-ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
-BRANDING_FILES := \
-	background.png \
-	firefox.icns \
-	disk.icns \
-	document.icns \
-	dsstore \
-	$(NULL)
-endif
-
-ifdef MOZ_WIDGET_GTK
-BRANDING_FILES := \
-	default16.png \
-	default32.png \
-	default48.png \
-	mozicon128.png \
-	$(NULL)
-endif
-
-BRANDING_DEST := $(DIST)/branding
-BRANDING_TARGET := export
-INSTALL_TARGETS += BRANDING
--- a/browser/branding/nightly/moz.build
+++ b/browser/branding/nightly/moz.build
@@ -4,12 +4,9 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += ['content', 'locales']
 
 DIST_SUBDIR = 'browser'
 export('DIST_SUBDIR')
 
-JS_PREFERENCE_FILES += [
-    'pref/firefox-branding.js',
-]
-
+include('../branding-common.mozbuild')
deleted file mode 100644
--- a/browser/branding/official/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-include $(topsrcdir)/config/config.mk
-
-ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
-BRANDING_FILES := \
-	firefox.ico \
-	document.ico \
-	branding.nsi \
-	appname.bmp \
-	bgintro.bmp \
-	clock.bmp \
-	particles.bmp \
-	pencil.bmp \
-	pencil-rtl.bmp \
-	wizHeader.bmp \
-	wizHeaderRTL.bmp \
-	wizWatermark.bmp \
-	newwindow.ico \
-	newtab.ico \
-	pbmode.ico \
-	$(NULL)
-endif
-
-ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
-BRANDING_FILES := \
-	background.png \
-	firefox.icns \
-	disk.icns \
-	document.icns \
-	dsstore \
-	$(NULL)
-endif
-
-ifdef MOZ_WIDGET_GTK
-BRANDING_FILES := \
-	default16.png \
-	default32.png \
-	default48.png \
-	mozicon128.png \
-	$(NULL)
-endif
-
-BRANDING_DEST := $(DIST)/branding
-BRANDING_TARGET := export
-INSTALL_TARGETS += BRANDING
--- a/browser/branding/official/moz.build
+++ b/browser/branding/official/moz.build
@@ -4,12 +4,9 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += ['content', 'locales']
 
 DIST_SUBDIR = 'browser'
 export('DIST_SUBDIR')
 
-JS_PREFERENCE_FILES += [
-    'pref/firefox-branding.js',
-]
-
+include('../branding-common.mozbuild')
deleted file mode 100644
--- a/browser/branding/unofficial/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-include $(topsrcdir)/config/config.mk
-
-ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
-BRANDING_FILES := \
-	firefox.ico \
-	document.ico \
-	branding.nsi \
-	appname.bmp \
-	bgintro.bmp \
-	clock.bmp \
-	particles.bmp \
-	pencil.bmp \
-	pencil-rtl.bmp \
-	wizHeader.bmp \
-	wizHeaderRTL.bmp \
-	wizWatermark.bmp \
-	newwindow.ico \
-	newtab.ico \
-	pbmode.ico \
-	$(NULL)
-endif
-
-ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
-BRANDING_FILES := \
-	background.png \
-	firefox.icns \
-	disk.icns \
-	document.icns \
-	dsstore \
-	$(NULL)
-endif
-
-ifdef MOZ_WIDGET_GTK
-BRANDING_FILES := \
-	default16.png \
-	default32.png \
-	default48.png \
-	mozicon128.png \
-	$(NULL)
-endif
-
-BRANDING_DEST := $(DIST)/branding
-BRANDING_TARGET := export
-INSTALL_TARGETS += BRANDING
--- a/browser/branding/unofficial/moz.build
+++ b/browser/branding/unofficial/moz.build
@@ -4,12 +4,9 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += ['content', 'locales']
 
 DIST_SUBDIR = 'browser'
 export('DIST_SUBDIR')
 
-JS_PREFERENCE_FILES += [
-    'pref/firefox-branding.js',
-]
-
+include('../branding-common.mozbuild')
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -23,16 +23,17 @@ from mozpack.copier import FilePurger
 from mozpack.manifests import (
     InstallManifest,
 )
 import mozpack.path as mozpath
 
 from .common import CommonBackend
 from ..frontend.data import (
     AndroidEclipseProjectData,
+    BrandingFiles,
     ConfigFileSubstitution,
     ContextDerived,
     ContextWrapped,
     Defines,
     DistFiles,
     DirectoryTraversal,
     Exports,
     ExternalLibrary,
@@ -372,16 +373,17 @@ class RecursiveMakeBackend(CommonBackend
         self._test_manifests = {}
 
         self.backend_input_files.add(mozpath.join(self.environment.topobjdir,
             'config', 'autoconf.mk'))
 
         self._install_manifests = {
             k: InstallManifest() for k in [
                 'dist_bin',
+                'dist_branding',
                 'dist_idl',
                 'dist_include',
                 'dist_public',
                 'dist_private',
                 'dist_sdk',
                 'dist_xpi-stage',
                 'tests',
                 'xpidl',
@@ -488,16 +490,19 @@ class RecursiveMakeBackend(CommonBackend
            method=obj.method))
 
         elif isinstance(obj, TestHarnessFiles):
             self._process_test_harness_files(obj, backend_file)
 
         elif isinstance(obj, Resources):
             self._process_resources(obj, obj.resources, backend_file)
 
+        elif isinstance(obj, BrandingFiles):
+            self._process_branding_files(obj, obj.files, backend_file)
+
         elif isinstance(obj, JsPreferenceFile):
             if obj.path.startswith('/'):
                 backend_file.write('PREF_JS_EXPORTS += $(topsrcdir)%s\n' % obj.path)
             else:
                 backend_file.write('PREF_JS_EXPORTS += $(srcdir)/%s\n' % obj.path)
 
         elif isinstance(obj, JARManifest):
             backend_file.write('JAR_MANIFEST := %s\n' % obj.path)
@@ -935,16 +940,32 @@ INSTALL_TARGETS += %(prefix)s
                 dep_file = mozpath.join(dep_path, mozpath.basename(source) + '.pp')
                 self._install_manifests['dist_bin'].add_preprocess(source, dest, dep_file, marker='%', defines=obj.defines)
             else:
                 self._install_manifests['dist_bin'].add_symlink(source, dest)
 
             if not os.path.exists(source):
                 raise Exception('File listed in RESOURCE_FILES does not exist: %s' % source)
 
+    def _process_branding_files(self, obj, files, backend_file):
+        for source, dest, flags in self._walk_hierarchy(obj, files):
+            if flags and flags.source:
+                source = mozpath.normpath(mozpath.join(obj.srcdir, flags.source))
+            if not os.path.exists(source):
+                raise Exception('File listed in BRANDING_FILES does not exist: %s' % source)
+
+            self._install_manifests['dist_branding'].add_symlink(source, dest)
+
+        # Also emit the necessary rules to create $(DIST)/branding during partial
+        # tree builds. The locale makefiles rely on this working.
+        backend_file.write('NONRECURSIVE_TARGETS += export\n')
+        backend_file.write('NONRECURSIVE_TARGETS_export += branding\n')
+        backend_file.write('NONRECURSIVE_TARGETS_export_branding_DIRECTORY = $(DEPTH)\n')
+        backend_file.write('NONRECURSIVE_TARGETS_export_branding_TARGETS += install-dist/branding\n')
+
     def _process_installation_target(self, obj, backend_file):
         # A few makefiles need to be able to override the following rules via
         # make XPI_NAME=blah commands, so we default to the lazy evaluation as
         # much as possible here to avoid breaking things.
         if obj.xpiname:
             backend_file.write('XPI_NAME = %s\n' % (obj.xpiname))
         if obj.subdir:
             backend_file.write('DIST_SUBDIR = %s\n' % (obj.subdir))
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -948,16 +948,37 @@ VARIABLES = {
         """, None),
 
     'LD_VERSION_SCRIPT': (unicode, unicode,
         """The linker version script for shared libraries.
 
         This variable can only be used on Linux.
         """, None),
 
+    'BRANDING_FILES': (HierarchicalStringListWithFlagsFactory({'source': unicode}), list,
+        """List of files to be installed into the branding directory.
+
+        ``BRANDING_FILES`` will copy (or symlink, if the platform supports it)
+        the contents of its files to the ``dist/branding`` directory. Files that
+        are destined for a subdirectory can be specified by accessing a field.
+        For example, to export ``foo.png`` to the top-level directory and
+        ``bar.png`` to the directory ``images/subdir``, append to
+        ``BRANDING_FILES`` like so::
+
+           BRANDING_FILES += ['foo.png']
+           BRANDING_FILES.images.subdir += ['bar.png']
+
+        If the source and destination have different file names, add the
+        destination name to the list and set the ``source`` property on the
+        entry, like so::
+
+           BRANDING_FILES.dir += ['baz.png']
+           BRANDING_FILES.dir['baz.png'].source = 'quux.png'
+        """, None),
+
     'RESOURCE_FILES': (HierarchicalStringListWithFlagsFactory({'preprocess': bool}), list,
         """List of resources to be exported, and in which subdirectories.
 
         ``RESOURCE_FILES`` is used to list the resource files to be exported to
         ``dist/bin/res``, but it can be used for other files as well. This variable
         behaves as a list when appending filenames for resources in the top-level
         directory. Files can also be appended to a field to indicate which
         subdirectory they should be exported to. For example, to export
--- a/python/mozbuild/mozbuild/frontend/data.py
+++ b/python/mozbuild/mozbuild/frontend/data.py
@@ -243,16 +243,30 @@ class Resources(ContextDerived):
         ContextDerived.__init__(self, context)
         self.resources = resources
         defs = {}
         defs.update(context.config.defines)
         if defines:
             defs.update(defines)
         self.defines = defs
 
+class BrandingFiles(ContextDerived):
+    """Sandbox container object for BRANDING_FILES, which is a
+    HierarchicalStringList.
+
+    We need an object derived from ContextDerived for use in the backend, so
+    this object fills that role. It just has a reference to the underlying
+    HierarchicalStringList, which is created when parsing BRANDING_FILES.
+    """
+    __slots__ = ('files')
+
+    def __init__(self, sandbox, files):
+        ContextDerived.__init__(self, sandbox)
+        self.files = files
+
 class JsPreferenceFile(ContextDerived):
     """Context derived container object for a Javascript preference file.
 
     Paths are assumed to be relative to the srcdir."""
     __slots__ = ('path')
 
     def __init__(self, context, path):
         ContextDerived.__init__(self, context)
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -20,16 +20,17 @@ from mozbuild.util import (
 )
 
 import mozpack.path as mozpath
 import manifestparser
 import reftest
 import mozinfo
 
 from .data import (
+    BrandingFiles,
     ConfigFileSubstitution,
     ContextWrapped,
     Defines,
     DistFiles,
     DirectoryTraversal,
     Exports,
     FinalTargetFiles,
     GeneratedEventWebIDLFile,
@@ -659,16 +660,20 @@ class TreeMetadataEmitter(LoggingMixin):
             for f in dist_files:
                 path = os.path.join(context.srcdir, f)
                 if not os.path.exists(path):
                     raise SandboxValidationError('File listed in DIST_FILES '
                         'does not exist: %s' % f, context)
 
             yield DistFiles(context, dist_files, context['FINAL_TARGET'])
 
+        branding_files = context.get('BRANDING_FILES')
+        if branding_files:
+            yield BrandingFiles(context, branding_files)
+
         self._handle_libraries(context)
 
         for obj in self._process_test_manifests(context):
             yield obj
 
         for obj in self._process_jar_manifests(context):
             yield obj
 
new file mode 100644
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/backend/data/branding-files/moz.build
@@ -0,0 +1,14 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+BRANDING_FILES += [
+    'app.ico',
+    'bar.ico',
+    'sub/quux.png',
+]
+BRANDING_FILES['app.ico'].source = 'bar.ico'
+
+BRANDING_FILES.icons += [
+    'foo.ico',
+]
+
new file mode 100644
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
+++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
@@ -400,16 +400,29 @@ class TestRecursiveMakeBackend(BackendTe
         self.assertIn('res/foo.res', m)
         self.assertIn('res/fonts/font1.ttf', m)
         self.assertIn('res/fonts/desktop/desktop2.ttf', m)
 
         self.assertIn('res/bar.res', m)
         self.assertIn('res/tests/test.manifest', m)
         self.assertIn('res/tests/extra.manifest', m)
 
+    def test_branding_files(self):
+        """Ensure BRANDING_FILES is handled properly."""
+        env = self._consume('branding-files', RecursiveMakeBackend)
+
+        #BRANDING_FILES should appear in the dist_branding install manifest.
+        m = InstallManifest(path=os.path.join(env.topobjdir,
+            '_build_manifests', 'install', 'dist_branding'))
+        self.assertEqual(len(m), 4)
+        self.assertIn('app.ico', m)
+        self.assertIn('bar.ico', m)
+        self.assertIn('quux.png', m)
+        self.assertIn('icons/foo.ico', m)
+
     def test_js_preference_files(self):
         """Ensure PREF_JS_EXPORTS is written out correctly."""
         env = self._consume('js_preference_files', RecursiveMakeBackend)
 
         backend_path = os.path.join(env.topobjdir, 'backend.mk')
         lines = [l.strip() for l in open(backend_path, 'rt').readlines()]
 
         # Avoid positional parameter and async related breakage
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/frontend/data/branding-files/moz.build
@@ -0,0 +1,15 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+BRANDING_FILES += [
+    'app.ico',
+    'bar.ico',
+    'baz.png',
+    'foo.xpm',
+]
+BRANDING_FILES['app.ico'].source = 'test/bar.ico'
+
+BRANDING_FILES.icons += [
+    'quux.icns',
+]
+
--- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py
@@ -5,16 +5,17 @@
 from __future__ import unicode_literals
 
 import os
 import unittest
 
 from mozunit import main
 
 from mozbuild.frontend.data import (
+    BrandingFiles,
     ConfigFileSubstitution,
     Defines,
     DistFiles,
     DirectoryTraversal,
     Exports,
     GeneratedFile,
     GeneratedInclude,
     GeneratedSources,
@@ -340,16 +341,33 @@ class TestEmitterBasic(unittest.TestCase
         self.assertIn('private', nspr._children)
         private = nspr._children['private']
         self.assertEqual(private._strings, ['pprio.res', 'pprthred.res'])
 
         self.assertIn('overwrite', resources._children)
         overwrite = resources._children['overwrite']
         self.assertEqual(overwrite._strings, ['new.res'])
 
+    def test_branding_files(self):
+        reader = self.reader('branding-files')
+        objs = self.read_topsrcdir(reader)
+
+        self.assertEqual(len(objs), 1)
+        self.assertIsInstance(objs[0], BrandingFiles)
+
+        files = objs[0].files
+
+        self.assertEqual(files._strings, ['app.ico', 'bar.ico', 'baz.png', 'foo.xpm'])
+        self.assertEqual(files['app.ico'].source, 'test/bar.ico')
+
+        self.assertIn('icons', files._children)
+        icons = files._children['icons']
+
+        self.assertEqual(icons._strings, ['quux.icns'])
+
     def test_preferences_js(self):
         reader = self.reader('js_preference_files')
         objs = self.read_topsrcdir(reader)
 
         prefs = [o.path for o in objs if isinstance(o, JsPreferenceFile)]
 
         prefsByDir = [
             'valid_val/prefs.js',
--- a/xulrunner/app/Makefile.in
+++ b/xulrunner/app/Makefile.in
@@ -33,29 +33,16 @@ libs::
 	$(INSTALL) $(IFLAGS1) $(DIST)/branding/default32.png $(DIST)/bin/chrome/icons/default
 	$(INSTALL) $(IFLAGS1) $(DIST)/branding/default48.png $(DIST)/bin/chrome/icons/default
 endif
 
 # XXX applications would need to supply this file
 #export:: brand.dtd.in
 #	$(call py_action,preprocessor,$(DEFINES) $(ACDEFINES) $^ -o brand.dtd)
 
-export::
-	$(NSINSTALL) -D $(DIST)/branding
-ifeq ($(OS_ARCH),WINNT)
-	cp $(srcdir)/xulrunner.ico   $(DIST)/branding/xulrunner.ico
-	cp $(srcdir)/xulrunner.ico   $(DIST)/branding/app.ico
-	cp $(srcdir)/document.ico  $(DIST)/branding/document.ico
-endif
-ifdef MOZ_WIDGET_GTK
-	cp $(srcdir)/default16.png   $(DIST)/branding/default16.png
-	cp $(srcdir)/default32.png   $(DIST)/branding/default32.png
-	cp $(srcdir)/default48.png   $(DIST)/branding/default48.png
-endif
-
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 
 FRAMEWORK_NAME = XUL
 FRAMEWORK_VERSION = $(MOZILLA_VERSION)
 
 FRAMEWORK_DIR = \
    $(DIST)/$(FRAMEWORK_NAME).framework/Versions/$(FRAMEWORK_VERSION)
 
--- a/xulrunner/app/moz.build
+++ b/xulrunner/app/moz.build
@@ -55,8 +55,22 @@ if CONFIG['OS_ARCH'] == 'WINNT':
 DISABLE_STL_WRAPPING = True
 
 FAIL_ON_WARNINGS = True
 
 JS_PREFERENCE_FILES += [
     'xulrunner.js',
 ]
 
+if CONFIG['OS_ARCH'] == 'WINNT':
+    BRANDING_FILES += [
+        'app.ico',
+        'document.ico',
+        'xulrunner.ico',
+    ]
+    BRANDING_FILES['app.ico'].source = 'xulrunner.ico'
+
+if CONFIG['MOZ_WIDGET_GTK']:
+    BRANDING_FILES += [
+        'default16.png',
+        'default32.png',
+        'default48.png',
+    ]