Bug 1469091 - Build the clang plugin as a host shared library. r=ted draft
authorMike Hommey <mh+mozilla@glandium.org>
Thu, 05 Jul 2018 14:58:09 +0900
changeset 815212 ccef69b2844d486a71472b53e0073f55bebcc814
parent 815151 aab71e0e93f97c3072cc7cd6967792410c814366
child 815213 aa6db6cf9c37bed147c905eb70e90257886cbac9
push id115463
push userbmo:mh+mozilla@glandium.org
push dateFri, 06 Jul 2018 22:11:39 +0000
reviewersted
bugs1469091
milestone63.0a1
Bug 1469091 - Build the clang plugin as a host shared library. r=ted This adds just enough host shared library support for this one use case, but also takes shortcuts, because fully supporting host shared library is a deep rabbit hole I'm not ready to take just to fix --enable-lto --enable-clang-plugin on mac builds. One downside is that one my machine the plugin now takes > 80s to build, instead of 15s before, thanks to the lack of unified sources.
build/clang-plugin/Makefile.in
build/clang-plugin/moz.build
build/templates.mozbuild
config/recurse.mk
config/rules.mk
moz.configure
python/mozbuild/mozbuild/backend/common.py
python/mozbuild/mozbuild/backend/configenvironment.py
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/build/clang-plugin/Makefile.in
+++ b/build/clang-plugin/Makefile.in
@@ -1,21 +1,16 @@
 # 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
 
-# In the current moz.build world, we need to override essentially every
-# variable to limit ourselves to what we need to build the clang plugin.
+HOST_LDFLAGS := $(LLVM_LDFLAGS) $(CLANG_LDFLAGS)
+
 ifneq ($(HOST_OS_ARCH),WINNT)
-DSO_LDOPTS := -shared
-endif
-
-ifeq ($(HOST_OS_ARCH)_$(OS_ARCH),Linux_Darwin)
-# Use the host compiler instead of the target compiler.
-CXX := $(HOST_CXX)
+HOST_LDFLAGS += -shared
 endif
 
 # Use the default OS X deployment target to enable using the libc++ headers
 # correctly.  Note that the binary produced here is a host tool and doesn't need
 # to be distributed.
 MACOSX_DEPLOYMENT_TARGET :=
--- a/build/clang-plugin/moz.build
+++ b/build/clang-plugin/moz.build
@@ -1,19 +1,19 @@
 # -*- Mode: python; 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/.
 
-SharedLibrary('clang-plugin')
+HostSharedLibrary('clang-plugin')
 
-SOURCES += ['!ThirdPartyPaths.cpp']
+HOST_SOURCES += ['!ThirdPartyPaths.cpp']
 
-UNIFIED_SOURCES += [
+HOST_SOURCES += [
     'ArithmeticArgChecker.cpp',
     'AssertAssignmentChecker.cpp',
     'CanRunScriptChecker.cpp',
     'CustomTypeAnnotation.cpp',
     'DanglingOnTemporaryChecker.cpp',
     'DiagnosticsMatcher.cpp',
     'ExplicitImplicitChecker.cpp',
     'ExplicitOperatorBoolChecker.cpp',
@@ -38,67 +38,53 @@ UNIFIED_SOURCES += [
     'RefCountedInsideLambdaChecker.cpp',
     'ScopeChecker.cpp',
     'SprintfLiteralChecker.cpp',
     'TrivialCtorDtorChecker.cpp',
     'VariableUsageHelpers.cpp',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
-    UNIFIED_SOURCES += [
+    HOST_SOURCES += [
         'LoadLibraryUsageChecker.cpp',
     ]
 
 if CONFIG['ENABLE_MOZSEARCH_PLUGIN']:
-    UNIFIED_SOURCES += [
+    HOST_SOURCES += [
         'mozsearch-plugin/FileOperations.cpp',
         'mozsearch-plugin/JSONFormatter.cpp',
         'mozsearch-plugin/MozsearchIndexer.cpp',
         'mozsearch-plugin/StringOperations.cpp',
     ]
 
 GENERATED_FILES += ['ThirdPartyPaths.cpp']
 third_party_paths = GENERATED_FILES['ThirdPartyPaths.cpp']
 third_party_paths.script = "ThirdPartyPaths.py:generate"
 third_party_paths.inputs = [
     '/tools/rewriting/ThirdPartyPaths.txt',
 ]
 
-DisableStlWrapping()
-NoVisibilityFlags()
+HOST_COMPILE_FLAGS['STL'] = []
+HOST_COMPILE_FLAGS['VISIBILITY'] = []
 
 # libc++ is required to build plugins against clang on OS X.
 if CONFIG['HOST_OS_ARCH'] == 'Darwin':
-    CXXFLAGS += ['-stdlib=libc++']
-    LDFLAGS += ['-lc++']
+    HOST_CXXFLAGS += ['-stdlib=libc++']
+    HOST_LDFLAGS += ['-lc++']
 
 DIRS += [
     'tests',
 ]
 
 
 # In the current moz.build world, we need to override essentially every
 # variable to limit ourselves to what we need to build the clang plugin.
 if CONFIG['HOST_OS_ARCH'] == 'WINNT':
     extra_cxxflags = ['-GR-', '-EHsc']
 else:
     extra_cxxflags = ['-fno-rtti', '-fno-exceptions']
 
 if CONFIG['LLVM_CXXFLAGS']:
-    COMPILE_FLAGS['OS_CXXFLAGS'] = CONFIG['LLVM_CXXFLAGS'] + extra_cxxflags
-
-COMPILE_FLAGS['CLANG_PLUGIN'] = []
-COMPILE_FLAGS['OPTIMIZE'] = []
-COMPILE_FLAGS['DEBUG'] = []
-COMPILE_FLAGS['OS_COMPILE_CXXFLAGS'] = []
-
-LINK_FLAGS['OS'] = CONFIG['LLVM_LDFLAGS'] + CONFIG['CLANG_LDFLAGS']
-# The ldflags above override most other categories.
-for var in ('LINKER', 'OPTIMIZE'):
-    LINK_FLAGS[var] = []
-
-if CONFIG['HOST_OS_ARCH'] == 'Linux' and CONFIG['OS_ARCH'] == 'Darwin':
-    # Don't pass OSX linker arguments.
-    LINK_FLAGS['FIX_LINK_PATHS'] = []
+    HOST_COMPILE_FLAGS['HOST_CXXFLAGS'] = CONFIG['LLVM_CXXFLAGS'] + extra_cxxflags
 
 # Avoid -DDEBUG=1 on the command line, which conflicts with a #define
 # DEBUG(...) in llvm headers.
 DEFINES['DEBUG'] = False
--- a/build/templates.mozbuild
+++ b/build/templates.mozbuild
@@ -99,16 +99,27 @@ def HostSimplePrograms(names, ext='.cpp'
     Those have a single source with the same base name as the executable.
     '''
     HOST_SIMPLE_PROGRAMS += names
     HOST_SOURCES += ['%s%s' % (name.replace('host_', ''), ext)
         for name in names]
 
 
 @template
+def HostSharedLibrary(name):
+    '''Template for build tools libraries.'''
+    if name != 'clang-plugin':
+        error('Please make sure host shared library support is complete '
+              'before using for something else than the clang plugin')
+
+    HOST_LIBRARY_NAME = name
+
+    FORCE_SHARED_LIB = True
+
+@template
 def HostLibrary(name):
     '''Template for build tools libraries.'''
     HOST_LIBRARY_NAME = name
 
 @template
 def HostRustLibrary(name, features=None):
     '''Template for host Rust libraries.'''
     HostLibrary(name)
--- a/config/recurse.mk
+++ b/config/recurse.mk
@@ -177,25 +177,25 @@ widget/android/export: mobile/android/ba
 
 # .xpt generation needs the xpidl lex/yacc files
 xpcom/xpidl/export: xpcom/idl-parser/xpidl/export
 
 # CSS2Properties.webidl needs ServoCSSPropList.py from layout/style
 dom/bindings/export: layout/style/export
 
 ifdef ENABLE_CLANG_PLUGIN
-$(filter-out config/host build/unix/stdc++compat/% build/clang-plugin/%,$(compile_targets)): build/clang-plugin/target build/clang-plugin/tests/target
-build/clang-plugin/tests/target: build/clang-plugin/target
+$(filter-out config/host build/unix/stdc++compat/% build/clang-plugin/%,$(compile_targets)): build/clang-plugin/host build/clang-plugin/tests/target
+build/clang-plugin/tests/target: build/clang-plugin/host
 endif
 
 # Interdependencies that moz.build world don't know about yet for compilation.
 # Note some others are hardcoded or "guessed" in recursivemake.py and emitter.py
 ifeq ($(MOZ_WIDGET_TOOLKIT),gtk3)
 toolkit/library/target: widget/gtk/mozgtk/gtk3/target
 endif
 ifdef MOZ_LDAP_XPCOM
 ldap/target: security/target mozglue/build/target
 toolkit/library/target: ldap/target
 endif
 endif
 # Most things are built during compile (target/host), but some things happen during export
 # Those need to depend on config/export for system wrappers.
-$(addprefix build/unix/stdc++compat/,target host) build/clang-plugin/target: config/export
+$(addprefix build/unix/stdc++compat/,target host) build/clang-plugin/host: config/export
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -178,17 +178,17 @@ endif
 ifdef MOZ_PROFILE_GENERATE
 $(foreach category,$(INSTALL_TARGETS),\
   $(eval $(category)_FILES := $(foreach file,$($(category)_FILES),$(if $(filter $(SIMPLE_PROGRAMS),$(notdir $(file))),,$(file)))))
 SIMPLE_PROGRAMS :=
 endif
 
 ifdef COMPILE_ENVIRONMENT
 ifndef TARGETS
-TARGETS			= $(LIBRARY) $(SHARED_LIBRARY) $(PROGRAM) $(SIMPLE_PROGRAMS) $(HOST_LIBRARY) $(HOST_PROGRAM) $(HOST_SIMPLE_PROGRAMS)
+TARGETS			= $(LIBRARY) $(SHARED_LIBRARY) $(PROGRAM) $(SIMPLE_PROGRAMS) $(HOST_LIBRARY) $(HOST_PROGRAM) $(HOST_SIMPLE_PROGRAMS) $(HOST_SHARED_LIBRARY)
 endif
 
 COBJS = $(notdir $(CSRCS:.c=.$(OBJ_SUFFIX)))
 SOBJS = $(notdir $(SSRCS:.S=.$(OBJ_SUFFIX)))
 # CPPSRCS can have different extensions (eg: .cpp, .cc)
 CPPOBJS = $(notdir $(addsuffix .$(OBJ_SUFFIX),$(basename $(CPPSRCS))))
 CMOBJS = $(notdir $(CMSRCS:.m=.$(OBJ_SUFFIX)))
 CMMOBJS = $(notdir $(CMMSRCS:.mm=.$(OBJ_SUFFIX)))
@@ -212,16 +212,17 @@ endif
 else
 LIBRARY :=
 SHARED_LIBRARY :=
 IMPORT_LIBRARY :=
 REAL_LIBRARY :=
 PROGRAM :=
 SIMPLE_PROGRAMS :=
 HOST_LIBRARY :=
+HOST_SHARED_LIBRARY :=
 HOST_PROGRAM :=
 HOST_SIMPLE_PROGRAMS :=
 endif
 
 ALL_TRASH = \
 	$(GARBAGE) $(TARGETS) $(OBJS) $(PROGOBJS) LOGS TAGS a.out \
 	$(filter-out $(ASFILES),$(OBJS:.$(OBJ_SUFFIX)=.s)) $(OBJS:.$(OBJ_SUFFIX)=.ii) \
 	$(OBJS:.$(OBJ_SUFFIX)=.i) $(OBJS:.$(OBJ_SUFFIX)=.i_o) \
@@ -434,17 +435,17 @@ everything::
 GLOBAL_DEPS += Makefile $(addprefix $(DEPTH)/config/,$(INCLUDED_AUTOCONF_MK)) $(MOZILLA_DIR)/config/config.mk
 
 ##############################################
 ifdef COMPILE_ENVIRONMENT
 OBJ_TARGETS = $(OBJS) $(PROGOBJS) $(HOST_OBJS) $(HOST_PROGOBJS)
 
 compile:: host target
 
-host:: $(HOST_LIBRARY) $(HOST_PROGRAM) $(HOST_SIMPLE_PROGRAMS) $(HOST_RUST_PROGRAMS) $(HOST_RUST_LIBRARY_FILE)
+host:: $(HOST_LIBRARY) $(HOST_PROGRAM) $(HOST_SIMPLE_PROGRAMS) $(HOST_RUST_PROGRAMS) $(HOST_RUST_LIBRARY_FILE) $(HOST_SHARED_LIBRARY)
 
 target:: $(LIBRARY) $(SHARED_LIBRARY) $(PROGRAM) $(SIMPLE_PROGRAMS) $(RUST_LIBRARY_FILE) $(RUST_PROGRAMS)
 
 ifndef LIBRARY
 ifdef OBJS
 target:: $(OBJS)
 endif
 endif
@@ -665,16 +666,26 @@ ifeq ($(OS_ARCH),WINNT)
 $(IMPORT_LIBRARY): $(SHARED_LIBRARY) ;
 endif
 
 $(HOST_LIBRARY): $(HOST_OBJS) Makefile
 	$(REPORT_BUILD)
 	$(RM) $@
 	$(HOST_AR) $(HOST_AR_FLAGS) $(HOST_OBJS)
 
+$(HOST_SHARED_LIBRARY): $(HOST_OBJS) Makefile
+	$(REPORT_BUILD)
+	$(RM) $@
+ifdef _MSC_VER
+	# /!\ We assume host and target are using the same compiler
+	$(LINKER) -NOLOGO -DLL -OUT:$@ $(HOST_OBJS) $(HOST_CXX_LDFLAGS) $(HOST_LDFLAGS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
+else
+	$(HOST_CXX) $(HOST_OUTOPTION)$@ $(HOST_OBJS) $(HOST_CXX_LDFLAGS) $(HOST_LDFLAGS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
+endif
+
 # On Darwin (Mac OS X), dwarf2 debugging uses debug info left in .o files,
 # so instead of deleting .o files after repacking them into a dylib, we make
 # symlinks back to the originals. The symlinks are a no-op for stabs debugging,
 # so no need to conditionalize on OS version or debugging format.
 
 $(SHARED_LIBRARY): $(OBJS) $(RESFILE) $(RUST_STATIC_LIB_FOR_SHARED_LIB) $(STATIC_LIBS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
 	$(REPORT_BUILD)
 ifndef INCREMENTAL_LINKER
--- a/moz.configure
+++ b/moz.configure
@@ -222,16 +222,18 @@ def library_name_info_template(host_or_t
 
     return library_name_info_impl
 
 host_library_name_info = library_name_info_template(host)
 library_name_info = library_name_info_template(target)
 
 set_config('DLL_PREFIX', library_name_info.dll.prefix)
 set_config('DLL_SUFFIX', library_name_info.dll.suffix)
+set_config('HOST_DLL_PREFIX', host_library_name_info.dll.prefix)
+set_config('HOST_DLL_SUFFIX', host_library_name_info.dll.suffix)
 set_config('LIB_PREFIX', library_name_info.lib.prefix)
 set_config('LIB_SUFFIX', library_name_info.lib.suffix)
 set_config('RUST_LIB_PREFIX', library_name_info.rust_lib.prefix)
 set_config('RUST_LIB_SUFFIX', library_name_info.rust_lib.suffix)
 set_config('OBJ_SUFFIX', library_name_info.obj.suffix)
 # Lots of compilation tests depend on this variable being present.
 add_old_configure_assignment('OBJ_SUFFIX', library_name_info.obj.suffix)
 set_config('IMPORT_LIB_SUFFIX', library_name_info.import_lib.suffix)
@@ -299,17 +301,18 @@ set_config('LINK_GTEST_DURING_COMPILE', 
 # ==============================================================
 option('--enable-ui-locale', default='en-US',
        help='Select the user interface locale (default: en-US)')
 
 set_config('MOZ_UI_LOCALE', depends('--enable-ui-locale')(lambda x: x))
 
 # clang-plugin location
 # ==============================================================
-@depends(library_name_info, check_build_environment, when='--enable-clang-plugin')
+@depends(host_library_name_info, check_build_environment,
+         when='--enable-clang-plugin')
 def clang_plugin_path(library_name_info, build_env):
     topobjdir = build_env.topobjdir
     if topobjdir.endswith('/js/src'):
         topobjdir = topobjdir[:-7]
     return os.path.abspath(
         os.path.join(topobjdir, 'build', 'clang-plugin',
                      '%sclang-plugin%s' % (library_name_info.dll.prefix,
                                            library_name_info.dll.suffix))
--- a/python/mozbuild/mozbuild/backend/common.py
+++ b/python/mozbuild/mozbuild/backend/common.py
@@ -23,16 +23,17 @@ from mozbuild.frontend.data import (
     ChromeManifestEntry,
     ConfigFileSubstitution,
     Exports,
     FinalTargetPreprocessedFiles,
     FinalTargetFiles,
     GeneratedSources,
     GnProjectData,
     HostLibrary,
+    HostGeneratedSources,
     HostRustLibrary,
     IPDLCollection,
     RustLibrary,
     SharedLibrary,
     StaticLibrary,
     UnifiedSources,
     XPIDLFile,
     WebIDLCollection,
@@ -149,17 +150,17 @@ class CommonBackend(BuildBackend):
         elif isinstance(obj, BaseProgram):
             self._binaries.programs.append(obj)
             return False
 
         elif isinstance(obj, SharedLibrary):
             self._binaries.shared_libraries.append(obj)
             return False
 
-        elif isinstance(obj, GeneratedSources):
+        elif isinstance(obj, (GeneratedSources, HostGeneratedSources)):
             self._handle_generated_sources(obj.files)
             return False
 
         elif isinstance(obj, Exports):
             objdir_files = [f.full_path for path, files in obj.files.walk() for f in files if isinstance(f, ObjDirPath)]
             if objdir_files:
                 self._handle_generated_sources(objdir_files)
             return False
--- a/python/mozbuild/mozbuild/backend/configenvironment.py
+++ b/python/mozbuild/mozbuild/backend/configenvironment.py
@@ -132,16 +132,18 @@ class ConfigEnvironment(object):
         self.lib_prefix = self.substs.get('LIB_PREFIX', '')
         self.rust_lib_prefix = self.substs.get('RUST_LIB_PREFIX', '')
         if 'LIB_SUFFIX' in self.substs:
             self.lib_suffix = '.%s' % self.substs['LIB_SUFFIX']
         if 'RUST_LIB_SUFFIX' in self.substs:
             self.rust_lib_suffix = '.%s' % self.substs['RUST_LIB_SUFFIX']
         self.dll_prefix = self.substs.get('DLL_PREFIX', '')
         self.dll_suffix = self.substs.get('DLL_SUFFIX', '')
+        self.host_dll_prefix = self.substs.get('HOST_DLL_PREFIX', '')
+        self.host_dll_suffix = self.substs.get('HOST_DLL_SUFFIX', '')
         if self.substs.get('IMPORT_LIB_SUFFIX'):
             self.import_prefix = self.lib_prefix
             self.import_suffix = '.%s' % self.substs['IMPORT_LIB_SUFFIX']
         else:
             self.import_prefix = self.dll_prefix
             self.import_suffix = self.dll_suffix
         self.bin_suffix = self.substs.get('BIN_SUFFIX', '')
 
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -38,16 +38,17 @@ from ..frontend.data import (
     Defines,
     DirectoryTraversal,
     ExternalLibrary,
     FinalTargetFiles,
     FinalTargetPreprocessedFiles,
     GeneratedFile,
     GeneratedSources,
     HostDefines,
+    HostGeneratedSources,
     HostLibrary,
     HostProgram,
     HostRustProgram,
     HostSimpleProgram,
     HostSources,
     InstallationTarget,
     JARManifest,
     Library,
@@ -55,16 +56,17 @@ from ..frontend.data import (
     LocalInclude,
     LocalizedFiles,
     LocalizedPreprocessedFiles,
     ObjdirFiles,
     ObjdirPreprocessedFiles,
     PerSourceFlag,
     Program,
     RustLibrary,
+    HostSharedLibrary,
     HostRustLibrary,
     RustProgram,
     RustTests,
     SharedLibrary,
     SimpleProgram,
     Sources,
     StaticLibrary,
     TestManifest,
@@ -472,26 +474,32 @@ class RecursiveMakeBackend(CommonBackend
                 variables.append('GARBAGE')
                 base = backend_file.objdir
             else:
                 base = backend_file.srcdir
             for f in sorted(obj.files):
                 f = mozpath.relpath(f, base)
                 for var in variables:
                     backend_file.write('%s += %s\n' % (var, f))
-        elif isinstance(obj, HostSources):
+        elif isinstance(obj, (HostSources, HostGeneratedSources)):
             suffix_map = {
                 '.c': 'HOST_CSRCS',
                 '.mm': 'HOST_CMMSRCS',
                 '.cpp': 'HOST_CPPSRCS',
             }
-            var = suffix_map[obj.canonical_suffix]
+            variables = [suffix_map[obj.canonical_suffix]]
+            if isinstance(obj, GeneratedSources):
+                variables.append('GARBAGE')
+                base = backend_file.objdir
+            else:
+                base = backend_file.srcdir
             for f in sorted(obj.files):
-                backend_file.write('%s += %s\n' % (
-                    var, mozpath.relpath(f, backend_file.srcdir)))
+                f = mozpath.relpath(f, base)
+                for var in variables:
+                    backend_file.write('%s += %s\n' % (var, f))
         elif isinstance(obj, VariablePassthru):
             # Sorted so output is consistent and we don't bump mtimes.
             for k, v in sorted(obj.variables.items()):
                 if k == 'HAS_MISC_RULE':
                     self._no_skip['misc'].add(backend_file.relobjdir)
                     continue
                 if isinstance(v, list):
                     for item in v:
@@ -677,16 +685,20 @@ 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, HostSharedLibrary):
+            self._process_host_shared_library(obj, backend_file)
+            self._process_linked_libraries(obj, backend_file)
+
         elif isinstance(obj, ObjdirFiles):
             self._process_objdir_files(obj, obj.files, backend_file)
 
         elif isinstance(obj, ObjdirPreprocessedFiles):
             self._process_final_target_pp_files(obj, obj.files, backend_file, 'OBJDIR_PP_FILES')
 
         elif isinstance(obj, LocalizedFiles):
             self._process_localized_files(obj, obj.files, backend_file)
@@ -1265,16 +1277,19 @@ class RecursiveMakeBackend(CommonBackend
         target_dir = mozpath.normpath(target_dir)
         backend_file.write('CARGO_TARGET_DIR := %s\n' % target_dir)
         if libdef.features:
             backend_file.write('%s := %s\n' % (libdef.FEATURES_VAR, ' '.join(libdef.features)))
 
     def _process_host_library(self, libdef, backend_file):
         backend_file.write('HOST_LIBRARY_NAME = %s\n' % libdef.basename)
 
+    def _process_host_shared_library(self, libdef, backend_file):
+        backend_file.write('HOST_SHARED_LIBRARY = %s\n' % libdef.lib_name)
+
     def _build_target_for_obj(self, obj):
         return '%s/%s' % (mozpath.relpath(obj.objdir,
             self.environment.topobjdir), obj.KIND)
 
     def _process_linked_libraries(self, obj, backend_file):
         def pretty_relpath(lib, name):
             return os.path.normpath(mozpath.join(mozpath.relpath(lib.objdir, obj.objdir),
                                                  name))
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -1514,17 +1514,17 @@ VARIABLES = {
 
     'USE_STATIC_LIBS': (bool, bool,
         """Whether the code in this directory is a built against the static
         runtime library.
 
         This variable only has an effect when building with MSVC.
         """),
 
-    'HOST_SOURCES': (ContextDerivedTypedList(SourcePath, StrictOrderingOnAppendList), list,
+    'HOST_SOURCES': (ContextDerivedTypedList(Path, StrictOrderingOnAppendList), list,
         """Source code files to compile with the host compiler.
 
         This variable contains a list of source code files to compile.
         with the host compiler.
         """),
 
     'HOST_LIBRARY_NAME': (unicode, unicode,
         """Name of target library generated when cross compiling.
--- a/python/mozbuild/mozbuild/frontend/data.py
+++ b/python/mozbuild/mozbuild/frontend/data.py
@@ -758,16 +758,32 @@ class SharedLibrary(Library):
                 self.symbols_file = '%s.def' % self.lib_name
             else:
                 self.symbols_file = '%s.symbols' % self.lib_name
         else:
             # Explicitly provided name.
             self.symbols_file = symbols_file
 
 
+class HostSharedLibrary(HostMixin, Library):
+    """Context derived container object for a host shared library.
+
+    This class supports less things than SharedLibrary does for target shared
+    libraries. Currently has enough build system support to build the clang
+    plugin."""
+    KIND = 'host'
+
+    def __init__(self, context, basename):
+        Library.__init__(self, context, basename)
+        self.lib_name = '%s%s%s' % (
+            context.config.host_dll_prefix,
+            self.basename,
+            context.config.host_dll_suffix,
+        )
+
 
 class ExternalLibrary(object):
     """Empty mixin for libraries built by an external build system."""
 
 
 class ExternalStaticLibrary(StaticLibrary, ExternalLibrary):
     """Context derived container for static libraries built by an external
     build system."""
@@ -944,16 +960,23 @@ class GeneratedSources(BaseSources):
 
 class HostSources(HostMixin, BaseSources):
     """Represents files to be compiled for the host during the build."""
 
     def __init__(self, context, files, canonical_suffix):
         BaseSources.__init__(self, context, files, canonical_suffix)
 
 
+class HostGeneratedSources(HostMixin, BaseSources):
+    """Represents generated files to be compiled for the host during the build."""
+
+    def __init__(self, context, files, canonical_suffix):
+        BaseSources.__init__(self, context, files, canonical_suffix)
+
+
 class UnifiedSources(BaseSources):
     """Represents files to be compiled in a unified fashion during the build."""
 
     __slots__ = (
         'have_unified_mapping',
         'unified_source_mapping'
     )
 
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -34,19 +34,21 @@ from .data import (
     FinalTargetFiles,
     FinalTargetPreprocessedFiles,
     GeneratedFile,
     GeneratedSources,
     GnProjectData,
     ExternalStaticLibrary,
     ExternalSharedLibrary,
     HostDefines,
+    HostGeneratedSources,
     HostLibrary,
     HostProgram,
     HostRustProgram,
+    HostSharedLibrary,
     HostSimpleProgram,
     HostSources,
     InstallationTarget,
     IPDLCollection,
     JARManifest,
     Library,
     Linkable,
     LocalInclude,
@@ -347,17 +349,19 @@ class TreeMetadataEmitter(LoggingMixin):
             self._link_library(context, obj, variable, path)
 
         # Link system libraries from OS_LIBS/HOST_OS_LIBS.
         for lib in context.get(variable.replace('USE', 'OS'), []):
             obj.link_system_library(lib)
 
         # We have to wait for all the self._link_library calls above to have
         # happened for obj.cxx_link to be final.
-        if not isinstance(obj, (StaticLibrary, HostLibrary,
+        # FIXME: Theoretically, HostSharedLibrary shouldn't be here (bug
+        # 1474022).
+        if not isinstance(obj, (StaticLibrary, HostLibrary, HostSharedLibrary,
                                 BaseRustProgram)) and obj.cxx_link:
             if context.config.substs.get(self.LIBSTDCXX_VAR[obj.KIND]):
                 self._link_library(context, obj, variable,
                                    self.STDCXXCOMPAT_NAME[obj.KIND])
             if obj.KIND == 'target':
                 for lib in context.config.substs.get('STLPORT_LIBS', []):
                     obj.link_system_library(lib)
 
@@ -640,16 +644,18 @@ class TreeMetadataEmitter(LoggingMixin):
         if host_libname:
             if host_libname == libname:
                 raise SandboxValidationError('LIBRARY_NAME and '
                     'HOST_LIBRARY_NAME must have a different value', context)
 
             is_rust_library = context.get('IS_RUST_LIBRARY')
             if is_rust_library:
                 lib = self._rust_library(context, host_libname, {}, cls=HostRustLibrary)
+            elif context.get('FORCE_SHARED_LIB'):
+                lib = HostSharedLibrary(context, host_libname)
             else:
                 lib = HostLibrary(context, host_libname)
             self._libs[host_libname].append(lib)
             self._linkage.append((context, lib, 'HOST_USE_LIBS'))
             host_linkables.append(lib)
 
         final_lib = context.get('FINAL_LIBRARY')
         if not libname and final_lib:
@@ -852,19 +858,18 @@ class TreeMetadataEmitter(LoggingMixin):
                     flags = context_srcs[f]
                     if flags:
                         all_flags[full_path] = flags
 
                 if isinstance(f, SourcePath) and not os.path.exists(full_path):
                     raise SandboxValidationError('File listed in %s does not '
                         'exist: \'%s\'' % (symbol, full_path), context)
 
-        # HOST_SOURCES and UNIFIED_SOURCES only take SourcePaths, so
-        # there should be no generated source in here
-        assert not gen_sources['HOST_SOURCES']
+        # UNIFIED_SOURCES only take SourcePaths, so there should be no
+        # generated source in here
         assert not gen_sources['UNIFIED_SOURCES']
 
         no_pgo = context.get('NO_PGO')
         no_pgo_sources = [f for f, flags in all_flags.iteritems()
                           if flags.no_pgo]
         if no_pgo:
             if no_pgo_sources:
                 raise SandboxValidationError('NO_PGO and SOURCES[...].no_pgo '
@@ -898,17 +903,17 @@ class TreeMetadataEmitter(LoggingMixin):
         def canonical_suffix_for_file(f):
             return canonicalized_suffix_map[mozpath.splitext(f)[1]]
 
         # A map from moz.build variables to the canonical suffixes of file
         # kinds that can be listed therein.
         all_suffixes = list(suffix_map.keys())
         varmap = dict(
             SOURCES=(Sources, GeneratedSources, all_suffixes),
-            HOST_SOURCES=(HostSources, None, ['.c', '.mm', '.cpp']),
+            HOST_SOURCES=(HostSources, HostGeneratedSources, ['.c', '.mm', '.cpp']),
             UNIFIED_SOURCES=(UnifiedSources, None, ['.c', '.mm', '.cpp']),
         )
         # Track whether there are any C++ source files.
         # Technically this won't do the right thing for SIMPLE_PROGRAMS in
         # a directory with mixed C and C++ source, but it's not that important.
         cxx_sources = defaultdict(bool)
 
         # Source files to track for linkables associated with this context.