Bug 910781 - add support for FINAL_TARGET_FILES; r=gps
authorJoshua Cranmer <Pidgeot18@gmail.com>
Wed, 23 Jul 2014 13:56:25 -0400
changeset 214753 0944ccd05e57caa0a5c919fbea6f3ac59a318459
parent 214712 970e11385295170eb53d18b37db21bd9968a2f66
child 214754 67c6993d405c89a32e1b76352eb07076b4a8f572
push id27795
push usercbook@mozilla.com
push dateMon, 10 Nov 2014 13:26:15 +0000
treeherdermozilla-central@1a09297482e1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs910781
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 910781 - add support for FINAL_TARGET_FILES; r=gps
Makefile.in
browser/app/Makefile.in
browser/app/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
--- a/Makefile.in
+++ b/Makefile.in
@@ -88,17 +88,17 @@ backend.RecursiveMakeBackend:
 Makefile: backend.RecursiveMakeBackend
 	@$(TOUCH) $@
 
 include backend.RecursiveMakeBackend.pp
 
 default:: backend.RecursiveMakeBackend
 
 install_manifests := \
-  $(addprefix dist/,bin idl include public private sdk) \
+  $(addprefix dist/,bin idl include public private sdk xpi-stage) \
   _tests \
   $(NULL)
 install_manifest_depends = \
   CLOBBER \
   $(configure_dir)/configure \
   config.status \
   backend.RecursiveMakeBackend \
   $(NULL)
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -64,29 +64,23 @@ endif
 ifdef MOZ_WIDGET_GTK
 libs::
 	$(INSTALL) $(IFLAGS1) $(DIST)/branding/mozicon128.png $(FINAL_TARGET)/icons
 	$(INSTALL) $(IFLAGS1) $(DIST)/branding/default16.png  $(FINAL_TARGET)/chrome/icons/default
 	$(INSTALL) $(IFLAGS1) $(DIST)/branding/default32.png  $(FINAL_TARGET)/chrome/icons/default
 	$(INSTALL) $(IFLAGS1) $(DIST)/branding/default48.png  $(FINAL_TARGET)/chrome/icons/default
 endif
 
-libs:: $(srcdir)/profile/prefs.js
-	$(INSTALL) $(IFLAGS1) $^ $(FINAL_TARGET)/defaults/profile
-
 ifndef LIBXUL_SDK
 # channel-prefs.js is handled separate from other prefs due to bug 756325
 libs:: $(srcdir)/profile/channel-prefs.js
 	$(NSINSTALL) -D $(DIST)/bin/defaults/pref
 	$(call py_action,preprocessor,-Fsubstitution $(PREF_PPFLAGS) $(ACDEFINES) $^ -o $(DIST)/bin/defaults/pref/channel-prefs.js)
 endif
 
-libs:: $(srcdir)/blocklist.xml
-	$(INSTALL) $(IFLAGS1) $^ $(FINAL_TARGET)
-
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 
 MAC_APP_NAME = $(MOZ_APP_DISPLAYNAME)
 
 ifdef MOZ_DEBUG
 MAC_APP_NAME := $(MAC_APP_NAME)Debug
 endif
 
--- a/browser/app/moz.build
+++ b/browser/app/moz.build
@@ -10,16 +10,19 @@ if CONFIG['OS_ARCH'] == 'WINNT' and CONF
     GeckoProgram(CONFIG['MOZ_APP_NAME'])
 else:
     GeckoProgram(CONFIG['MOZ_APP_NAME'], msvcrt='static')
 
 SOURCES += [
     'nsBrowserApp.cpp',
 ]
 
+FINAL_TARGET_FILES += ['blocklist.xml']
+FINAL_TARGET_FILES.defaults.profile += ['profile/prefs.js']
+
 DEFINES['APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 
 for var in ('MOZILLA_OFFICIAL', 'LIBXUL_SDK'):
     if CONFIG[var]:
         DEFINES[var] = True
 
 GENERATED_INCLUDES += [
     '/build',
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -31,16 +31,17 @@ from ..frontend.data import (
     AndroidEclipseProjectData,
     ConfigFileSubstitution,
     ContextDerived,
     ContextWrapped,
     Defines,
     DirectoryTraversal,
     Exports,
     ExternalLibrary,
+    FinalTargetFiles,
     GeneratedInclude,
     HostLibrary,
     HostProgram,
     HostSimpleProgram,
     InstallationTarget,
     IPDLFile,
     JARManifest,
     JavaJarData,
@@ -302,16 +303,17 @@ class RecursiveMakeBackend(CommonBackend
         self._install_manifests = {
             k: InstallManifest() for k in [
                 'dist_bin',
                 'dist_idl',
                 'dist_include',
                 'dist_public',
                 'dist_private',
                 'dist_sdk',
+                'dist_xpi-stage',
                 'tests',
                 'xpidl',
             ]}
 
         self._traversal = RecursiveMakeTraversal()
         self._compile_graph = defaultdict(set)
 
         self._may_skip = {
@@ -474,16 +476,18 @@ class RecursiveMakeBackend(CommonBackend
         elif isinstance(obj, StaticLibrary):
             self._process_static_library(obj, backend_file)
             self._process_linked_libraries(obj, backend_file)
 
         elif isinstance(obj, HostLibrary):
             self._process_host_library(obj, backend_file)
             self._process_linked_libraries(obj, backend_file)
 
+        elif isinstance(obj, FinalTargetFiles):
+            self._process_final_target_files(obj, obj.files, obj.target)
         else:
             return
         obj.ack()
 
     def _fill_root_mk(self):
         """
         Create two files, root.mk and root-deps.mk, the first containing
         convenience variables, and the other dependency definitions for a
@@ -1198,16 +1202,32 @@ INSTALL_TARGETS += %(prefix)s
             if obj.KIND == 'target':
                 backend_file.write_once('OS_LIBS += %s\n' % lib)
             else:
                 backend_file.write_once('HOST_EXTRA_LIBS += %s\n' % lib)
 
         # Process library-based defines
         self._process_defines(obj.defines, backend_file)
 
+    def _process_final_target_files(self, obj, files, target):
+        if target.startswith('dist/bin'):
+            install_manifest = self._install_manifests['dist_bin']
+            reltarget = mozpath.relpath(target, 'dist/bin')
+        elif target.startswith('dist/xpi-stage'):
+            install_manifest = self._install_manifests['dist_xpi-stage']
+            reltarget = mozpath.relpath(target, 'dist/xpi-stage')
+        else:
+            raise Exception("Cannot install to " + target)
+
+        for path, strings in files.walk():
+            for f in strings:
+                source = mozpath.normpath(os.path.join(obj.srcdir, f))
+                dest = mozpath.join(reltarget, path, mozpath.basename(f))
+                install_manifest.add_symlink(source, dest)
+
     def _write_manifests(self, dest, manifests):
         man_dir = mozpath.join(self.environment.topobjdir, '_build_manifests',
             dest)
 
         # We have a purger for the manifests themselves to ensure legacy
         # manifests are deleted.
         purger = FilePurger()
 
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -496,16 +496,31 @@ VARIABLES = {
         and read the frontend file there. If there is no frontend file, an error
         is raised.
 
         Values are relative paths. They can be multiple directory levels
         above or below. Use ``..`` for parent directories and ``/`` for path
         delimiters.
         """, None),
 
+    'FINAL_TARGET_FILES': (HierarchicalStringList, list,
+        """List of files to be installed into the application directory.
+
+        ``FINAL_TARGET_FILES`` will copy (or symlink, if the platform supports it)
+        the contents of its files to the directory specified by
+        ``FINAL_TARGET`` (typically ``dist/bin``). Files that are destined for a
+        subdirectory can be specified by accessing a field, or as a dict access.
+        For example, to export ``foo.png`` to the top-level directory and
+        ``bar.svg`` to the directory ``images/do-not-use``, append to
+        ``FINAL_TARGET_FILES`` like so::
+
+           FINAL_TARGET_FILES += ['foo.png']
+           FINAL_TARGET_FILES.images['do-not-use'] += ['bar.svg']
+        """, None),
+
     'DISABLE_STL_WRAPPING': (bool, bool,
         """Disable the wrappers for STL which allow it to work with C++ exceptions
         disabled.
         """, None),
 
     'EXTRA_COMPONENTS': (StrictOrderingOnAppendList, list,
         """Additional component files to distribute.
 
--- a/python/mozbuild/mozbuild/frontend/data.py
+++ b/python/mozbuild/mozbuild/frontend/data.py
@@ -748,16 +748,32 @@ class InstallationTarget(ContextDerived)
         """Returns whether or not the target is not derived from the default
         given xpiname and subdir."""
 
         return FinalTargetValue(dict(
             XPI_NAME=self.xpiname,
             DIST_SUBDIR=self.subdir)) == self.target
 
 
+class FinalTargetFiles(ContextDerived):
+    """Sandbox container object for FINAL_TARGET_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 FINAL_TARGET_FILES.
+    """
+    __slots__ = ('files', 'target')
+
+    def __init__(self, sandbox, files, target):
+        ContextDerived.__init__(self, sandbox)
+        self.files = files
+        self.target = target
+
+
 class ClassPathEntry(object):
     """Represents a classpathentry in an Android Eclipse project."""
 
     __slots__ = (
         'dstdir',
         'srcdir',
         'path',
         'exclude_patterns',
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -25,16 +25,17 @@ import reftest
 import mozinfo
 
 from .data import (
     ConfigFileSubstitution,
     ContextWrapped,
     Defines,
     DirectoryTraversal,
     Exports,
+    FinalTargetFiles,
     GeneratedEventWebIDLFile,
     GeneratedInclude,
     GeneratedWebIDLFile,
     ExampleWebIDLInterface,
     ExternalStaticLibrary,
     ExternalSharedLibrary,
     HeaderFileSubstitution,
     HostLibrary,
@@ -595,16 +596,20 @@ class TreeMetadataEmitter(LoggingMixin):
                 raise SandboxValidationError('Path specified in LOCAL_INCLUDES '
                     'does not exist: %s (resolved to %s)' % (local_include, actual_include), context)
             yield LocalInclude(context, local_include)
 
         if context.get('FINAL_TARGET') or context.get('XPI_NAME') or \
                 context.get('DIST_SUBDIR'):
             yield InstallationTarget(context)
 
+        final_target_files = context.get('FINAL_TARGET_FILES')
+        if final_target_files:
+            yield FinalTargetFiles(context, final_target_files, context['FINAL_TARGET'])
+
         host_libname = context.get('HOST_LIBRARY_NAME')
         libname = context.get('LIBRARY_NAME')
 
         if host_libname:
             if host_libname == libname:
                 raise SandboxValidationError('LIBRARY_NAME and '
                     'HOST_LIBRARY_NAME must have a different value', context)
             lib = HostLibrary(context, host_libname)