Bug 1405811 - Move host compilation flags to mozbuild. r=mshal
authorChris Manchester <cmanchester@mozilla.com>
Mon, 02 Oct 2017 11:41:41 -0700
changeset 389371 08b6c9db493b2feb411f0667353be11733313e1d
parent 389370 f34bd383fea056b9278a85f74efe9c2e97aa0ab3
child 389372 91806d4db6441a4d4c2d7d79867057b996d81631
push id54465
push usercmanchester@mozilla.com
push dateTue, 31 Oct 2017 22:11:11 +0000
treeherderautoland@08b6c9db493b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmshal
bugs1405811
milestone58.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 1405811 - Move host compilation flags to mozbuild. r=mshal 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 = {