Bug 1405811 - Move host compilation flags to mozbuild. draft
authorChris Manchester <cmanchester@mozilla.com>
Mon, 02 Oct 2017 11:41:41 -0700
changeset 688011 8a61c7aecdb3d5846563cef2b00c52686cc4f3db
parent 688010 f2a2c3901b18037f5490748b98fd7a66e4a5de38
child 737772 a74448937769ded68a8c7e0ce94b0662c42f4d22
push id86640
push userbmo:cmanchester@mozilla.com
push dateFri, 27 Oct 2017 23:36:20 +0000
bugs1405811
milestone58.0a1
Bug 1405811 - Move host compilation flags to mozbuild. MozReview-Commit-ID: 7I5IHM65eaU
config/config.mk
python/mozbuild/mozbuild/frontend/context.py
python/mozbuild/mozbuild/frontend/emitter.py
python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build
python/mozbuild/mozbuild/test/backend/test_recursivemake.py
python/mozbuild/mozbuild/test/frontend/data/host-compile-flags/moz.build
python/mozbuild/mozbuild/test/frontend/data/host-compile-flags/test1.c
python/mozbuild/mozbuild/test/frontend/data/host-defines/moz.build
python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build
python/mozbuild/mozbuild/test/frontend/test_emitter.py
--- a/config/config.mk
+++ b/config/config.mk
@@ -250,40 +250,24 @@ INCLUDES = \
 include $(MOZILLA_DIR)/config/static-checking-config.mk
 
 LDFLAGS		= $(OS_LDFLAGS) $(MOZBUILD_LDFLAGS) $(MOZ_FIX_LINK_PATHS)
 
 ifdef MOZ_OPTIMIZE
 LDFLAGS		+= $(MOZ_OPTIMIZE_LDFLAGS)
 endif # MOZ_OPTIMIZE
 
-HOST_CFLAGS	+= $(_DEPEND_CFLAGS)
-HOST_CXXFLAGS	+= $(_DEPEND_CFLAGS)
-ifdef CROSS_COMPILE
-HOST_CFLAGS	+= $(HOST_OPTIMIZE_FLAGS)
-HOST_CXXFLAGS	+= $(HOST_OPTIMIZE_FLAGS)
-else
-ifdef MOZ_OPTIMIZE
-HOST_CFLAGS	+= $(MOZ_OPTIMIZE_FLAGS)
-HOST_CXXFLAGS	+= $(MOZ_OPTIMIZE_FLAGS)
-endif # MOZ_OPTIMIZE
-endif # CROSS_COMPILE
-
 COMPILE_CFLAGS	= $(COMPUTED_CFLAGS) $(PGO_CFLAGS) $(_DEPEND_CFLAGS) $(MK_COMPILE_DEFINES)
 COMPILE_CXXFLAGS = $(COMPUTED_CXXFLAGS) $(PGO_CFLAGS) $(_DEPEND_CFLAGS) $(MK_COMPILE_DEFINES)
 COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(MOZBUILD_CMFLAGS)
 COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) $(MOZBUILD_CMMFLAGS)
 ASFLAGS += $(MOZBUILD_ASFLAGS)
 
-ifndef CROSS_COMPILE
-HOST_CFLAGS += $(RTL_FLAGS)
-endif
-
-HOST_CFLAGS += $(HOST_DEFINES) $(MOZBUILD_HOST_CFLAGS)
-HOST_CXXFLAGS += $(HOST_DEFINES) $(MOZBUILD_HOST_CXXFLAGS)
+HOST_CFLAGS = $(COMPUTED_HOST_CFLAGS) $(_DEPEND_CFLAGS)
+HOST_CXXFLAGS = $(COMPUTED_HOST_CXXFLAGS) $(_DEPEND_CFLAGS)
 
 # We only add color flags if neither the flag to disable color
 # (e.g. "-fno-color-diagnostics" nor a flag to control color
 # (e.g. "-fcolor-diagnostics=never") is present.
 define colorize_flags
 ifeq (,$(filter $(COLOR_CFLAGS:-f%=-fno-%),$$(1))$(findstring $(COLOR_CFLAGS),$$(1)))
 $(1) += $(COLOR_CFLAGS)
 endif
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -295,17 +295,61 @@ class InitializedDefines(ContextDerivedV
     def __init__(self, context, value=None):
         OrderedDict.__init__(self)
         for define in context.config.substs.get('MOZ_DEBUG_DEFINES', ()):
             self[define] = 1
         if value:
             self.update(value)
 
 
-class CompileFlags(ContextDerivedValue, dict):
+class BaseCompileFlags(ContextDerivedValue, dict):
+    def __init__(self, context):
+        self._context = context
+        self._known_keys = set(k for k, v, _ in self.flag_variables)
+
+        # Providing defaults here doesn't play well with multiple templates
+        # modifying COMPILE_FLAGS from the same moz.build, because the merge
+        # done after the template runs can't tell which values coming from
+        # a template were set and which were provided as defaults.
+        template_name = getattr(context, 'template', None)
+        if template_name in (None, 'Gyp'):
+            dict.__init__(self, ((k, v if v is None else TypedList(unicode)(v))
+                                 for k, v, _ in self.flag_variables))
+        else:
+            dict.__init__(self)
+
+
+class HostCompileFlags(BaseCompileFlags):
+    def __init__(self, context):
+        self._context = context
+
+        self.flag_variables = (
+            ('HOST_CXXFLAGS', context.config.substs.get('HOST_CXXFLAGS'),
+             ('HOST_CXXFLAGS',)),
+            ('HOST_CFLAGS', context.config.substs.get('HOST_CFLAGS'),
+             ('HOST_CFLAGS',)),
+            ('HOST_OPTIMIZE', self._optimize_flags(),
+             ('HOST_CFLAGS', 'HOST_CXXFLAGS')),
+            ('RTL', None, ('HOST_CFLAGS',)),
+            ('HOST_DEFINES', None, ('HOST_CFLAGS', 'HOST_CXXFLAGS')),
+            ('MOZBUILD_HOST_CFLAGS', [], ('HOST_CFLAGS',)),
+            ('MOZBUILD_HOST_CXXFLAGS', [], ('HOST_CXXFLAGS',)),
+        )
+        BaseCompileFlags.__init__(self, context)
+
+    def _optimize_flags(self):
+        optimize_flags = []
+        if self._context.config.substs.get('CROSS_COMPILE'):
+            optimize_flags += self._context.config.substs.get('HOST_OPTIMIZE_FLAGS')
+        elif self._context.config.substs.get('MOZ_OPTIMIZE'):
+            optimize_flags += self._context.config.substs.get('MOZ_OPTIMIZE_FLAGS')
+        return optimize_flags
+
+
+class CompileFlags(BaseCompileFlags):
     def __init__(self, context):
         main_src_dir = mozpath.dirname(context.main_path)
         self._context = context
 
         self.flag_variables = (
             ('STL', context.config.substs.get('STL_FLAGS'), ('CXXFLAGS',)),
             ('VISIBILITY', context.config.substs.get('VISIBILITY_FLAGS'),
              ('CXXFLAGS', 'CFLAGS')),
@@ -343,28 +387,18 @@ class CompileFlags(ContextDerivedValue, 
              ('CFLAGS', 'CXXFLAGS', 'CXX_LDFLAGS', 'C_LDFLAGS')),
             ('FRAMEPTR', context.config.substs.get('MOZ_FRAMEPTR_FLAGS'),
              ('CFLAGS', 'CXXFLAGS', 'CXX_LDFLAGS', 'C_LDFLAGS')),
             ('WARNINGS_AS_ERRORS', self._warnings_as_errors(),
              ('CXXFLAGS', 'CFLAGS', 'CXX_LDFLAGS', 'C_LDFLAGS')),
             ('MOZBUILD_CFLAGS', None, ('CFLAGS',)),
             ('MOZBUILD_CXXFLAGS', None, ('CXXFLAGS',)),
         )
-        self._known_keys = set(k for k, v, _ in self.flag_variables)
-
-        # Providing defaults here doesn't play well with multiple templates
-        # modifying COMPILE_FLAGS from the same moz.build, because the merge
-        # done after the template runs can't tell which values coming from
-        # a template were set and which were provided as defaults.
-        template_name = getattr(context, 'template', None)
-        if template_name in (None, 'Gyp'):
-            dict.__init__(self, ((k, v if v is None else TypedList(unicode)(v))
-                                 for k, v, _ in self.flag_variables))
-        else:
-            dict.__init__(self)
+
+        BaseCompileFlags.__init__(self, context)
 
     def _debug_flags(self):
         if (self._context.config.substs.get('MOZ_DEBUG') or
             self._context.config.substs.get('MOZ_DEBUG_SYMBOLS')):
             return self._context.config.substs.get('MOZ_DEBUG_FLAGS', '').split()
         return []
 
     def _warnings_as_errors(self):
@@ -1885,16 +1919,21 @@ VARIABLES = {
         """Flags passed to the C++ compiler for all of the C++ source files
            declared in this directory.
 
            Note that the ordering of flags matters here; these flags will be
            added to the compiler's command line in the same order as they
            appear in the moz.build file.
         """),
 
+    'HOST_COMPILE_FLAGS': (HostCompileFlags, dict,
+        """Recipe for host compile flags for this context. Not to be manipulated
+        directly.
+        """),
+
     'HOST_DEFINES': (InitializedDefines, dict,
         """Dictionary of compiler defines to declare for host compilation.
         See ``DEFINES`` for specifics.
         """),
 
     'CMFLAGS': (List, list,
         """Flags passed to the Objective-C compiler for all of the Objective-C
            source files declared in this directory.
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -126,16 +126,17 @@ class TreeMetadataEmitter(LoggingMixin):
         for k, v in mozinfo.info.items():
             if isinstance(k, unicode):
                 k = k.encode('ascii')
             self.info[k] = v
 
         self._libs = OrderedDefaultDict(list)
         self._binaries = OrderedDict()
         self._compile_dirs = set()
+        self._host_compile_dirs = set()
         self._compile_flags = dict()
         self._linkage = []
         self._static_linking_shared = set()
         self._crate_verified_local = set()
         self._crate_directories = dict()
 
         # Keep track of external paths (third party build systems), starting
         # from what we run a subconfigure in. We'll eliminate some directories
@@ -769,18 +770,24 @@ class TreeMetadataEmitter(LoggingMixin):
         # Note the linkables are not emitted in this function, but much later,
         # after aggregation (because of e.g. USE_LIBS processing).
         if not (linkables or host_linkables):
             return
 
         # Avoid emitting compile flags for directories only containing rust
         # libraries. Emitted compile flags are only relevant to C/C++ sources
         # for the time being.
-        if not all(isinstance(l, (RustLibrary, HostRustLibrary))
-                   for l in linkables + host_linkables):
+        if not all(isinstance(l, (RustLibrary)) for l in linkables):
+            self._compile_dirs.add(context.objdir)
+        if host_linkables and not all(isinstance(l, HostRustLibrary) for l in host_linkables):
+            self._host_compile_dirs.add(context.objdir)
+            # TODO: objdirs with only host things in them shouldn't need target
+            # flags, but there's at least one Makefile.in (in
+            # build/unix/elfhack) that relies on the value of LDFLAGS being
+            # passed to one-off rules.
             self._compile_dirs.add(context.objdir)
 
         sources = defaultdict(list)
         gen_sources = defaultdict(list)
         all_flags = {}
         for symbol in ('SOURCES', 'HOST_SOURCES', 'UNIFIED_SOURCES'):
             srcs = sources[symbol]
             gen_srcs = gen_sources[symbol]
@@ -913,16 +920,17 @@ class TreeMetadataEmitter(LoggingMixin):
         # We always emit a directory traversal descriptor. This is needed by
         # the recursive make backend.
         for o in self._emit_directory_traversal_from_context(context): yield o
 
         for obj in self._process_xpidl(context):
             yield obj
 
         computed_flags = ComputedFlags(context, context['COMPILE_FLAGS'])
+        computed_host_flags = ComputedFlags(context, context['HOST_COMPILE_FLAGS'])
 
         # Proxy some variables as-is until we have richer classes to represent
         # them. We should aim to keep this set small because it violates the
         # desired abstraction of the build definition away from makefiles.
         passthru = VariablePassthru(context)
         varlist = [
             'ANDROID_APK_NAME',
             'ANDROID_APK_PACKAGE',
@@ -943,25 +951,28 @@ class TreeMetadataEmitter(LoggingMixin):
                 passthru.variables[v] = context[v]
 
         if context.config.substs.get('OS_TARGET') == 'WINNT' and \
                 context['DELAYLOAD_DLLS']:
             context['LDFLAGS'].extend([('-DELAYLOAD:%s' % dll)
                 for dll in context['DELAYLOAD_DLLS']])
             context['OS_LIBS'].append('delayimp')
 
-        for v in ['CMFLAGS', 'CMMFLAGS', 'ASFLAGS', 'LDFLAGS',
-                  'HOST_CFLAGS', 'HOST_CXXFLAGS']:
+        for v in ['CMFLAGS', 'CMMFLAGS', 'ASFLAGS', 'LDFLAGS']:
             if v in context and context[v]:
                 passthru.variables['MOZBUILD_' + v] = context[v]
 
         for v in ['CXXFLAGS', 'CFLAGS']:
             if v in context and context[v]:
                 computed_flags.resolve_flags('MOZBUILD_%s' % v, context[v])
 
+        for v in ['HOST_CXXFLAGS', 'HOST_CFLAGS']:
+            if v in context and context[v]:
+                computed_host_flags.resolve_flags('MOZBUILD_%s' % v, context[v])
+
         dist_install = context['DIST_INSTALL']
         if dist_install is True:
             passthru.variables['DIST_INSTALL'] = True
         elif dist_install is False:
             passthru.variables['NO_DIST_INSTALL'] = True
 
         # Ideally, this should be done in templates, but this is difficult at
         # the moment because USE_STATIC_LIBS can be set after a template
@@ -970,49 +981,49 @@ class TreeMetadataEmitter(LoggingMixin):
         if (context.config.substs.get('OS_ARCH') == 'WINNT' and
                 not context.config.substs.get('GNU_CC')):
             use_static_lib = (context.get('USE_STATIC_LIBS') and
                               not context.config.substs.get('MOZ_ASAN'))
             rtl_flag = '-MT' if use_static_lib else '-MD'
             if (context.config.substs.get('MOZ_DEBUG') and
                     not context.config.substs.get('MOZ_NO_DEBUG_RTL')):
                 rtl_flag += 'd'
-            # Use a list, like MOZBUILD_*FLAGS variables
-            passthru.variables['RTL_FLAGS'] = [rtl_flag]
             computed_flags.resolve_flags('RTL', [rtl_flag])
+            if not context.config.substs.get('CROSS_COMPILE'):
+                computed_host_flags.resolve_flags('RTL', [rtl_flag])
 
         generated_files = set()
         for obj in self._process_generated_files(context):
             for f in obj.outputs:
                 generated_files.add(f)
             yield obj
 
         for path in context['CONFIGURE_SUBST_FILES']:
             sub = self._create_substitution(ConfigFileSubstitution, context,
                 path)
             generated_files.add(str(sub.relpath))
             yield sub
 
-        for defines_var, cls in (('DEFINES', Defines),
-                                 ('HOST_DEFINES', HostDefines)):
+        for defines_var, cls, backend_flags in (('DEFINES', Defines, computed_flags),
+                                                ('HOST_DEFINES', HostDefines, computed_host_flags)):
             defines = context.get(defines_var)
             if defines:
                 defines_obj = cls(context, defines)
-                yield defines_obj
+                if isinstance(defines_obj, Defines):
+                    # DEFINES have consumers outside the compile command line,
+                    # HOST_DEFINES do not.
+                    yield defines_obj
             else:
                 # If we don't have explicitly set defines we need to make sure
                 # initialized values if present end up in computed flags.
                 defines_obj = cls(context, context[defines_var])
 
-            if isinstance(defines_obj, Defines):
-                defines_from_obj = list(defines_obj.get_defines())
-                if defines_from_obj:
-                    computed_flags.resolve_flags('DEFINES',
-                                                 defines_from_obj)
-
+            defines_from_obj = list(defines_obj.get_defines())
+            if defines_from_obj:
+                backend_flags.resolve_flags(defines_var, defines_from_obj)
 
         simple_lists = [
             ('GENERATED_EVENTS_WEBIDL_FILES', GeneratedEventWebIDLFile),
             ('GENERATED_WEBIDL_FILES', GeneratedWebIDLFile),
             ('IPDL_SOURCES', IPDLFile),
             ('PREPROCESSED_TEST_WEBIDL_FILES', PreprocessedTestWebIDLFile),
             ('PREPROCESSED_WEBIDL_FILES', PreprocessedWebIDLFile),
             ('TEST_WEBIDL_FILES', TestWebIDLFile),
@@ -1173,16 +1184,18 @@ class TreeMetadataEmitter(LoggingMixin):
         if android_extra_packages:
             yield AndroidExtraPackages(context, android_extra_packages)
 
         if passthru.variables:
             yield passthru
 
         if context.objdir in self._compile_dirs:
             self._compile_flags[context.objdir] = computed_flags
+        if context.objdir in self._host_compile_dirs:
+            yield computed_host_flags
 
 
     def _create_substitution(self, cls, context, path):
         sub = cls(context)
         sub.input_path = '%s.in' % path.full_path
         sub.output_path = path.translated
         sub.relpath = path
 
--- a/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build
+++ b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build
@@ -5,11 +5,9 @@
 DELAYLOAD_DLLS = ['foo.dll', 'bar.dll']
 
 RCFILE = 'foo.rc'
 RESFILE = 'bar.res'
 RCINCLUDE = 'bar.rc'
 DEFFILE = 'baz.def'
 
 LDFLAGS += ['-ld flag with spaces', '-x']
-HOST_CFLAGS += ['-funroll-loops', '-wall']
-HOST_CXXFLAGS += ['-funroll-loops-harder', '-wall-day-everyday']
 WIN32_EXE_LDFLAGS += ['-subsystem:console']
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
+++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
@@ -329,24 +329,16 @@ class TestRecursiveMakeBackend(BackendTe
                 'DEFFILE := baz.def',
             ],
             'MOZBUILD_LDFLAGS': [
                 "MOZBUILD_LDFLAGS += '-ld flag with spaces'",
                 'MOZBUILD_LDFLAGS += -x',
                 'MOZBUILD_LDFLAGS += -DELAYLOAD:foo.dll',
                 'MOZBUILD_LDFLAGS += -DELAYLOAD:bar.dll',
             ],
-            'MOZBUILD_HOST_CFLAGS': [
-                'MOZBUILD_HOST_CFLAGS += -funroll-loops',
-                'MOZBUILD_HOST_CFLAGS += -wall',
-            ],
-            'MOZBUILD_HOST_CXXFLAGS': [
-                'MOZBUILD_HOST_CXXFLAGS += -funroll-loops-harder',
-                'MOZBUILD_HOST_CXXFLAGS += -wall-day-everyday',
-            ],
             'WIN32_EXE_LDFLAGS': [
                 'WIN32_EXE_LDFLAGS += -subsystem:console',
             ],
         }
 
         for var, val in expected.items():
             # print("test_variable_passthru[%s]" % (var))
             found = [str for str in lines if str.startswith(var)]
@@ -687,29 +679,16 @@ class TestRecursiveMakeBackend(BackendTe
         lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
 
         var = 'DEFINES'
         defines = [val for val in lines if val.startswith(var)]
 
         expected = ['DEFINES += -DFOO \'-DBAZ="ab\'\\\'\'cd"\' -UQUX -DBAR=7 -DVALUE=xyz']
         self.assertEqual(defines, expected)
 
-    def test_host_defines(self):
-        """Test that HOST_DEFINES are written to backend.mk correctly."""
-        env = self._consume('host-defines', RecursiveMakeBackend)
-
-        backend_path = mozpath.join(env.topobjdir, 'backend.mk')
-        lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
-
-        var = 'HOST_DEFINES'
-        defines = [val for val in lines if val.startswith(var)]
-
-        expected = ['HOST_DEFINES += -DFOO \'-DBAZ="ab\'\\\'\'cd"\' -UQUX -DBAR=7 -DVALUE=xyz']
-        self.assertEqual(defines, expected)
-
     def test_local_includes(self):
         """Test that LOCAL_INCLUDES are written to backend.mk correctly."""
         env = self._consume('local_includes', RecursiveMakeBackend)
 
         backend_path = mozpath.join(env.topobjdir, 'backend.mk')
         lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
 
         expected = [
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/frontend/data/host-compile-flags/moz.build
@@ -0,0 +1,25 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+@template
+def HostLibrary(name):
+    '''Template for libraries.'''
+    HOST_LIBRARY_NAME = name
+
+HostLibrary('dummy')
+
+HOST_SOURCES += ['test1.c']
+
+value = 'xyz'
+HOST_DEFINES = {
+    'FOO': True,
+}
+
+HOST_DEFINES['BAZ'] = '"abcd"'
+HOST_DEFINES.update({
+    'BAR': 7,
+    'VALUE': value,
+    'QUX': False,
+})
+
+HOST_CFLAGS += ['-funroll-loops', '-host-arg']
\ No newline at end of file
new file mode 100644
deleted file mode 100644
--- a/python/mozbuild/mozbuild/test/frontend/data/host-defines/moz.build
+++ /dev/null
@@ -1,14 +0,0 @@
-# Any copyright is dedicated to the Public Domain.
-# http://creativecommons.org/publicdomain/zero/1.0/
-
-value = 'xyz'
-HOST_DEFINES = {
-    'FOO': True,
-}
-
-HOST_DEFINES['BAZ'] = '"abcd"'
-HOST_DEFINES.update({
-    'BAR': 7,
-    'VALUE': value,
-    'QUX': False,
-})
--- a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build
+++ b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build
@@ -7,11 +7,9 @@ DIST_INSTALL = False
 DELAYLOAD_DLLS = ['foo.dll', 'bar.dll']
 
 RCFILE = 'foo.rc'
 RESFILE = 'bar.res'
 RCINCLUDE = 'bar.rc'
 DEFFILE = 'baz.def'
 
 LDFLAGS += ['-framework Foo', '-x']
-HOST_CFLAGS += ['-funroll-loops', '-wall']
-HOST_CXXFLAGS += ['-funroll-loops-harder', '-wall-day-everyday']
 WIN32_EXE_LDFLAGS += ['-subsystem:console']
--- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py
@@ -185,19 +185,16 @@ class TestEmitterBasic(unittest.TestCase
         wanted = {
             'NO_DIST_INSTALL': True,
             'RCFILE': 'foo.rc',
             'RESFILE': 'bar.res',
             'RCINCLUDE': 'bar.rc',
             'DEFFILE': 'baz.def',
             'MOZBUILD_LDFLAGS': ['-framework Foo', '-x', '-DELAYLOAD:foo.dll',
                                  '-DELAYLOAD:bar.dll'],
-            'MOZBUILD_HOST_CFLAGS': ['-funroll-loops', '-wall'],
-            'MOZBUILD_HOST_CXXFLAGS': ['-funroll-loops-harder',
-                                       '-wall-day-everyday'],
             'WIN32_EXE_LDFLAGS': ['-subsystem:console'],
         }
 
         variables = objs[0].variables
         maxDiff = self.maxDiff
         self.maxDiff = None
         self.assertEqual(wanted, variables)
         self.maxDiff = maxDiff
@@ -227,16 +224,68 @@ class TestEmitterBasic(unittest.TestCase
         reader = self.reader('compile-flags', extra_substs={
             'MOZ_DEBUG_FLAGS': '-g',
             'MOZ_DEBUG_SYMBOLS': '',
         })
         sources, lib, flags = self.read_topsrcdir(reader)
         self.assertIsInstance(flags, ComputedFlags)
         self.assertEqual(flags.flags['DEBUG'], [])
 
+    def test_host_compile_flags(self):
+        reader = self.reader('host-compile-flags', extra_substs={
+            'HOST_CXXFLAGS': ['-Wall', '-Werror'],
+            'HOST_CFLAGS': ['-Werror', '-Wall'],
+        })
+        sources, flags, lib, target_flags = self.read_topsrcdir(reader)
+        self.assertIsInstance(flags, ComputedFlags)
+        self.assertEqual(flags.flags['HOST_CXXFLAGS'], reader.config.substs['HOST_CXXFLAGS'])
+        self.assertEqual(flags.flags['HOST_CFLAGS'], reader.config.substs['HOST_CFLAGS'])
+        self.assertEqual(set(flags.flags['HOST_DEFINES']),
+                         set(['-DFOO', '-DBAZ="abcd"', '-UQUX', '-DBAR=7', '-DVALUE=xyz']))
+        self.assertEqual(flags.flags['MOZBUILD_HOST_CFLAGS'], ['-funroll-loops', '-host-arg'])
+        self.assertEqual(flags.flags['MOZBUILD_HOST_CXXFLAGS'], [])
+
+    def test_host_no_optimize_flags(self):
+        reader = self.reader('host-compile-flags', extra_substs={
+            'MOZ_OPTIMIZE': '',
+            'MOZ_OPTIMIZE_FLAGS': ['-O2'],
+        })
+        sources, flags, lib, target_flags = self.read_topsrcdir(reader)
+        self.assertIsInstance(flags, ComputedFlags)
+        self.assertEqual(flags.flags['HOST_OPTIMIZE'], [])
+
+    def test_host_optimize_flags(self):
+        reader = self.reader('host-compile-flags', extra_substs={
+            'MOZ_OPTIMIZE': '1',
+            'MOZ_OPTIMIZE_FLAGS': ['-O2'],
+        })
+        sources, flags, lib, target_flags = self.read_topsrcdir(reader)
+        self.assertIsInstance(flags, ComputedFlags)
+        self.assertEqual(flags.flags['HOST_OPTIMIZE'], ['-O2'])
+
+    def test_cross_optimize_flags(self):
+        reader = self.reader('host-compile-flags', extra_substs={
+            'MOZ_OPTIMIZE': '1',
+            'MOZ_OPTIMIZE_FLAGS': ['-O2'],
+            'HOST_OPTIMIZE_FLAGS': ['-O3'],
+            'CROSS_COMPILE': '1',
+        })
+        sources, flags, lib, target_flags = self.read_topsrcdir(reader)
+        self.assertIsInstance(flags, ComputedFlags)
+        self.assertEqual(flags.flags['HOST_OPTIMIZE'], ['-O3'])
+
+    def test_host_rtl_flag(self):
+        reader = self.reader('host-compile-flags', extra_substs={
+            'OS_ARCH': 'WINNT',
+            'MOZ_DEBUG': '1',
+        })
+        sources, flags, lib, target_flags = self.read_topsrcdir(reader)
+        self.assertIsInstance(flags, ComputedFlags)
+        self.assertEqual(flags.flags['RTL'], ['-MDd'])
+
     def test_compile_flags_validation(self):
         reader = self.reader('compile-flags-field-validation')
 
         with self.assertRaisesRegexp(BuildReaderError, 'Invalid value.'):
             self.read_topsrcdir(reader)
 
         reader = self.reader('compile-flags-type-validation')
         with self.assertRaisesRegexp(BuildReaderError,
@@ -848,35 +897,16 @@ class TestEmitterBasic(unittest.TestCase
             'BAZ': '"abcd"',
             'FOO': True,
             'VALUE': 'xyz',
             'QUX': False,
         }
 
         self.assertEqual(defines, expected)
 
-    def test_host_defines(self):
-        reader = self.reader('host-defines')
-        objs = self.read_topsrcdir(reader)
-
-        defines = {}
-        for o in objs:
-            if isinstance(o, HostDefines):
-                defines = o.defines
-
-        expected = {
-            'BAR': 7,
-            'BAZ': '"abcd"',
-            'FOO': True,
-            'VALUE': 'xyz',
-            'QUX': False,
-        }
-
-        self.assertEqual(defines, expected)
-
     def test_jar_manifests(self):
         reader = self.reader('jar-manifests')
         objs = self.read_topsrcdir(reader)
 
         self.assertEqual(len(objs), 1)
         for obj in objs:
             self.assertIsInstance(obj, JARManifest)
             self.assertIsInstance(obj.path, Path)
@@ -1010,21 +1040,25 @@ class TestEmitterBasic(unittest.TestCase
                 sources.files,
                 [mozpath.join(reader.config.topobjdir, f) for f in files])
 
     def test_host_sources(self):
         """Test that HOST_SOURCES works properly."""
         reader = self.reader('host-sources')
         objs = self.read_topsrcdir(reader)
 
+        # This objdir will generate target flags.
         flags = objs.pop()
         self.assertIsInstance(flags, ComputedFlags)
         # The second to last object is a Linkable
         linkable = objs.pop()
         self.assertTrue(linkable.cxx_link)
+        # This objdir will also generate host flags.
+        flags = objs.pop()
+        self.assertIsInstance(flags, ComputedFlags)
         self.assertEqual(len(objs), 3)
         for o in objs:
             self.assertIsInstance(o, HostSources)
 
         suffix_map = {obj.canonical_suffix: obj for obj in objs}
         self.assertEqual(len(suffix_map), 3)
 
         expected = {