Bug 910781 - add support for FINAL_TARGET_FILES; r=gps
authorJoshua Cranmer <Pidgeot18@gmail.com>
Wed, 23 Jul 2014 13:56:25 -0400
changeset 226067 0944ccd05e57caa0a5c919fbea6f3ac59a318459
parent 226066 970e11385295170eb53d18b37db21bd9968a2f66
child 226068 67c6993d405c89a32e1b76352eb07076b4a8f572
push id31
push userdburns@mozilla.com
push dateSat, 08 Nov 2014 00:12:59 +0000
reviewersgps
bugs910781
milestone36.0a1
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)