Bug 1469091 - Build the clang plugin as a host shared library. r=ted
authorMike Hommey <mh+mozilla@glandium.org>
Thu, 05 Jul 2018 14:58:09 +0900
changeset 425426 531b966781e6b2c086a798b18cec231a26a9d3e5
parent 425425 79ef83e82742de2911844ab13315072dc2306a74
child 425427 4bd2d2037953ef6b5d56b80a68a2b7792cc32cc6
push id105054
push userdluca@mozilla.com
push dateSat, 07 Jul 2018 10:38:00 +0000
treeherdermozilla-inbound@2430d2986d5a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted
bugs1469091
milestone63.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 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.