Bug 1036894 part 6 - Emit SimplePrograms for CPP_UNIT_TESTs, and make the corresponding moz.build config look like that of SIMPLE_PROGRAMS. r=gps
authorMike Hommey <mh+mozilla@glandium.org>
Wed, 23 Jul 2014 13:29:44 +0900
changeset 205315 ce8aee89742083a277d3208ffa001c582240cd88
parent 205314 73520ece4b1a40cf557b673f1b8197f801c62e62
child 205316 8679d6be0eb560a3b1f02ce296d19a9079eedb4b
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-esr52@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs1036894
milestone34.0a1
Bug 1036894 part 6 - Emit SimplePrograms for CPP_UNIT_TESTs, and make the corresponding moz.build config look like that of SIMPLE_PROGRAMS. r=gps
config/rules.mk
content/base/test/moz.build
content/canvas/compiledtest/moz.build
content/media/compiledtest/moz.build
content/media/webaudio/compiledtest/moz.build
dom/audiochannel/tests/moz.build
editor/txmgr/tests/moz.build
intl/lwbrk/tests/moz.build
media/libcubeb/tests/moz.build
media/mtransport/test/moz.build
media/webrtc/signaling/test/moz.build
memory/mozalloc/tests/moz.build
mfbt/tests/moz.build
netwerk/test/moz.build
python/mozbuild/mozbuild/backend/recursivemake.py
python/mozbuild/mozbuild/frontend/data.py
python/mozbuild/mozbuild/frontend/emitter.py
python/mozbuild/mozbuild/frontend/sandbox_symbols.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/variable-passthru/moz.build
python/mozbuild/mozbuild/test/frontend/test_emitter.py
security/manager/ssl/tests/compiled/moz.build
startupcache/test/moz.build
storage/test/moz.build
toolkit/components/places/tests/cpp/moz.build
toolkit/components/url-classifier/tests/moz.build
widget/tests/moz.build
xpcom/tests/moz.build
xpcom/tests/windows/moz.build
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -76,39 +76,37 @@ ifdef ENABLE_TESTS
 DIRS += $(TEST_DIRS)
 
 ifdef CPP_UNIT_TESTS
 ifdef COMPILE_ENVIRONMENT
 
 # Compile the tests to $(DIST)/bin.  Make lots of niceties available by default
 # through TestHarness.h, by modifying the list of includes and the libs against
 # which stuff links.
-CPPSRCS += $(CPP_UNIT_TESTS)
-CPP_UNIT_TEST_BINS := $(CPP_UNIT_TESTS:.cpp=$(BIN_SUFFIX))
-SIMPLE_PROGRAMS += $(CPP_UNIT_TEST_BINS)
+SIMPLE_PROGRAMS += $(CPP_UNIT_TESTS)
 INCLUDES += -I$(DIST)/include/testing
 LIBS += $(XPCOM_GLUE_LDOPTS) $(NSPR_LIBS)
 
 ifndef MOZ_PROFILE_GENERATE
-libs:: $(CPP_UNIT_TEST_BINS) $(call mkdir_deps,$(DIST)/cppunittests)
-	$(NSINSTALL) $(CPP_UNIT_TEST_BINS) $(DIST)/cppunittests
+libs:: $(CPP_UNIT_TESTS) $(call mkdir_deps,$(DIST)/cppunittests)
+	$(NSINSTALL) $(CPP_UNIT_TESTS) $(DIST)/cppunittests
 endif
 
 run-cppunittests::
-	@$(PYTHON) $(topsrcdir)/testing/runcppunittests.py --xre-path=$(DIST)/bin --symbols-path=$(DIST)/crashreporter-symbols $(subst .cpp,$(BIN_SUFFIX),$(CPP_UNIT_TESTS))
+	@$(PYTHON) $(topsrcdir)/testing/runcppunittests.py --xre-path=$(DIST)/bin --symbols-path=$(DIST)/crashreporter-symbols $(CPP_UNIT_TESTS)
 
 cppunittests-remote: DM_TRANS?=adb
 cppunittests-remote:
 	@if [ '${TEST_DEVICE}' != '' -o '$(DM_TRANS)' = 'adb' ]; then \
 		$(PYTHON) -u $(topsrcdir)/testing/remotecppunittests.py \
 			--xre-path=$(DEPTH)/dist/bin \
 			--localLib=$(DEPTH)/dist/$(MOZ_APP_NAME) \
 			--dm_trans=$(DM_TRANS) \
 			--deviceIP=${TEST_DEVICE} \
-			$(subst .cpp,$(BIN_SUFFIX),$(CPP_UNIT_TESTS)) $(EXTRA_TEST_ARGS); \
+			$(CPP_UNIT_TESTS) $(EXTRA_TEST_ARGS); \
 	else \
 		echo 'please prepare your host with environment variables for TEST_DEVICE'; \
 	fi
 
 endif # COMPILE_ENVIRONMENT
 endif # CPP_UNIT_TESTS
 
 .PHONY: check
--- a/content/base/test/moz.build
+++ b/content/base/test/moz.build
@@ -11,22 +11,24 @@ TEST_TOOL_DIRS += [
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 
 # FIXME/bug 575918: out-of-process xpcshell is broken on OS X
 if CONFIG['OS_ARCH'] != 'Darwin':
     XPCSHELL_TESTS_MANIFESTS += ['unit_ipc/xpcshell.ini']
 
 CPP_UNIT_TESTS += [
-    'TestCSPParser.cpp',
-    'TestGetURL.cpp',
-    'TestNativeXMLHttpRequest.cpp',
-    'TestPlainTextSerializer.cpp',
+    'TestCSPParser',
+    'TestGetURL',
+    'TestNativeXMLHttpRequest',
+    'TestPlainTextSerializer',
 ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 MOCHITEST_MANIFESTS += [
     'chrome/mochitest.ini',
     'mochitest.ini',
 ]
 # OOP tests don't work on Windows (bug 763081) or native-fennec
 # (see Bug 774939). App permission checks are also disabled on
 # anything but B2G (Bug 900707).
 if CONFIG['MOZ_CHILD_PERMISSIONS']:
--- a/content/canvas/compiledtest/moz.build
+++ b/content/canvas/compiledtest/moz.build
@@ -1,16 +1,18 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 CPP_UNIT_TESTS += [
-    'TestWebGLElementArrayCache.cpp',
+    'TestWebGLElementArrayCache',
 ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 FAIL_ON_WARNINGS = True
 
 LOCAL_INCLUDES += [
     '../src',
 ]
 
--- a/content/media/compiledtest/moz.build
+++ b/content/media/compiledtest/moz.build
@@ -1,16 +1,18 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 CPP_UNIT_TESTS += [
-    'TestAudioMixer.cpp',
+    'TestAudioMixer',
 ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 FAIL_ON_WARNINGS = True
 
 LOCAL_INCLUDES += [
     '..',
 ]
 
--- a/content/media/webaudio/compiledtest/moz.build
+++ b/content/media/webaudio/compiledtest/moz.build
@@ -1,16 +1,18 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 CPP_UNIT_TESTS += [
-    'TestAudioEventTimeline.cpp',
+    'TestAudioEventTimeline',
 ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 FAIL_ON_WARNINGS = True
 
 LOCAL_INCLUDES += [
     '..',
 ]
 
--- a/dom/audiochannel/tests/moz.build
+++ b/dom/audiochannel/tests/moz.build
@@ -1,17 +1,19 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 CPP_UNIT_TESTS += [
-    'TestAudioChannelService.cpp',
+    'TestAudioChannelService',
 ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 if CONFIG['OS_ARCH'] == 'WINNT':
     DEFINES['NOMINMAX'] = True
 
 MOCHITEST_MANIFESTS += ['mochitest.ini']
 
 FAIL_ON_WARNINGS = True
 
--- a/editor/txmgr/tests/moz.build
+++ b/editor/txmgr/tests/moz.build
@@ -1,12 +1,14 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 CPP_UNIT_TESTS += [
-    'TestTXMgr.cpp',
+    'TestTXMgr',
 ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 FAIL_ON_WARNINGS = True
 
--- a/intl/lwbrk/tests/moz.build
+++ b/intl/lwbrk/tests/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 CPP_UNIT_TESTS += [
-    'TestLineBreak.cpp',
+    'TestLineBreak',
 ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 LOCAL_INCLUDES += [
     '../public',
 ]
 
--- a/media/libcubeb/tests/moz.build
+++ b/media/libcubeb/tests/moz.build
@@ -1,23 +1,25 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 CPP_UNIT_TESTS += [
-  'test_tone.cpp'
+  'test_tone'
 ]
 
 if CONFIG['OS_TARGET'] != 'Android':
   CPP_UNIT_TESTS += [
-    'test_audio.cpp',
-    'test_latency.cpp',
-    'test_sanity.cpp'
+    'test_audio',
+    'test_latency',
+    'test_sanity'
   ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 LOCAL_INCLUDES += [
     '../include'
 ]
 
 FAIL_ON_WARNINGS = True
 
--- a/media/mtransport/test/moz.build
+++ b/media/mtransport/test/moz.build
@@ -1,33 +1,35 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     CPP_UNIT_TESTS += [
-        'buffered_stun_socket_unittest.cpp',
-        'ice_unittest.cpp',
-        'nrappkit_unittest.cpp',
-        'rlogringbuffer_unittest.cpp',
-        'runnable_utils_unittest.cpp',
-        'simpletokenbucket_unittest.cpp',
-        'sockettransportservice_unittest.cpp',
-        'TestSyncRunnable.cpp',
-        'transport_unittests.cpp',
-        'turn_unittest.cpp',
+        'buffered_stun_socket_unittest',
+        'ice_unittest',
+        'nrappkit_unittest',
+        'rlogringbuffer_unittest',
+        'runnable_utils_unittest',
+        'simpletokenbucket_unittest',
+        'sockettransportservice_unittest',
+        'TestSyncRunnable',
+        'transport_unittests',
+        'turn_unittest',
     ]
 
     if CONFIG['MOZ_SCTP']:
         CPP_UNIT_TESTS += [
-            'sctp_unittest.cpp',
+            'sctp_unittest',
         ]
 
+    SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 FAIL_ON_WARNINGS = True
 
 for var in ('HAVE_STRDUP', 'NR_SOCKET_IS_VOID_PTR', 'SCTP_DEBUG', 'INET'):
     DEFINES[var] = True
 
 if CONFIG['OS_TARGET'] == 'Android':
     LOCAL_INCLUDES += [
         '/media/mtransport/third_party/nrappkit/src/port/android/include',
--- a/media/webrtc/signaling/test/moz.build
+++ b/media/webrtc/signaling/test/moz.build
@@ -1,21 +1,24 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     CPP_UNIT_TESTS += [
-        'mediaconduit_unittests.cpp',
-        'mediapipeline_unittest.cpp',
-        'sdp_unittests.cpp',
-        'signaling_unittests.cpp',
+        'mediaconduit_unittests',
+        'mediapipeline_unittest',
+        'sdp_unittests',
+        'signaling_unittests',
     ]
+
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 if CONFIG['OS_TARGET'] in ('Darwin', 'Android'):
     DEFINES['GTEST_USE_OWN_TR1_TUPLE'] = 1
 
 for var in ('USE_FAKE_MEDIA_STREAMS', 'USE_FAKE_PCOBSERVER',
             'NR_SOCKET_IS_VOID_PTR', 'HAVE_STRDUP'):
     DEFINES[var] = True
--- a/memory/mozalloc/tests/moz.build
+++ b/memory/mozalloc/tests/moz.build
@@ -1,9 +1,11 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 CPP_UNIT_TESTS += [
-    'TestVolatileBuffer.cpp',
+    'TestVolatileBuffer',
 ]
+
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -1,44 +1,46 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 CPP_UNIT_TESTS += [
-    'TestArrayUtils.cpp',
-    'TestAtomics.cpp',
-    'TestBinarySearch.cpp',
-    'TestBloomFilter.cpp',
-    'TestCasting.cpp',
-    'TestCeilingFloor.cpp',
-    'TestCheckedInt.cpp',
-    'TestCountPopulation.cpp',
-    'TestCountZeroes.cpp',
-    'TestEndian.cpp',
-    'TestEnumSet.cpp',
-    'TestFloatingPoint.cpp',
-    'TestIntegerPrintfMacros.cpp',
-    'TestMacroArgs.cpp',
-    'TestMacroForEach.cpp',
-    'TestPair.cpp',
-    'TestRollingMean.cpp',
-    'TestSHA1.cpp',
-    'TestTypedEnum.cpp',
-    'TestTypeTraits.cpp',
-    'TestUniquePtr.cpp',
-    'TestWeakPtr.cpp',
+    'TestArrayUtils',
+    'TestAtomics',
+    'TestBinarySearch',
+    'TestBloomFilter',
+    'TestCasting',
+    'TestCeilingFloor',
+    'TestCheckedInt',
+    'TestCountPopulation',
+    'TestCountZeroes',
+    'TestEndian',
+    'TestEnumSet',
+    'TestFloatingPoint',
+    'TestIntegerPrintfMacros',
+    'TestMacroArgs',
+    'TestMacroForEach',
+    'TestPair',
+    'TestRollingMean',
+    'TestSHA1',
+    'TestTypedEnum',
+    'TestTypeTraits',
+    'TestUniquePtr',
+    'TestWeakPtr',
 ]
 
 if not CONFIG['MOZ_ASAN']:
     CPP_UNIT_TESTS += [
-        'TestPoisonArea.cpp',
+        'TestPoisonArea',
     ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 # Since we link directly with MFBT object files, define IMPL_MFBT
 DEFINES['IMPL_MFBT'] = True
 
 DISABLE_STL_WRAPPING = True
 
 if CONFIG['_MSC_VER']:
   CXXFLAGS += [
     '-wd4275', # non dll-interface class used as base for dll-interface class
--- a/netwerk/test/moz.build
+++ b/netwerk/test/moz.build
@@ -47,16 +47,18 @@ SIMPLE_PROGRAMS = [
 #    TestUDPSocketProvider',
 #]
 
 SOURCES += [
     '%s.cpp' % s for s in SIMPLE_PROGRAMS
 ]
 
 CPP_UNIT_TESTS += [
-    'TestSTSParser.cpp',
-    'TestUDPSocket.cpp',
+    'TestSTSParser',
+    'TestUDPSocket',
 ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 RESOURCE_FILES += [
     'urlparse.dat',
     'urlparse_unx.dat',
 ]
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -431,17 +431,17 @@ class RecursiveMakeBackend(CommonBackend
             self._process_program(obj.program, backend_file)
             self._process_linked_libraries(obj, backend_file)
 
         elif isinstance(obj, HostProgram):
             self._process_host_program(obj.program, backend_file)
             self._process_linked_libraries(obj, backend_file)
 
         elif isinstance(obj, SimpleProgram):
-            self._process_simple_program(obj.program, backend_file)
+            self._process_simple_program(obj, backend_file)
             self._process_linked_libraries(obj, backend_file)
 
         elif isinstance(obj, HostSimpleProgram):
             self._process_host_simple_program(obj.program, backend_file)
             self._process_linked_libraries(obj, backend_file)
 
         elif isinstance(obj, LocalInclude):
             self._process_local_include(obj.path, backend_file)
@@ -1012,18 +1012,21 @@ class RecursiveMakeBackend(CommonBackend
         ))
 
     def _process_program(self, program, backend_file):
         backend_file.write('PROGRAM = %s\n' % program)
 
     def _process_host_program(self, program, backend_file):
         backend_file.write('HOST_PROGRAM = %s\n' % program)
 
-    def _process_simple_program(self, program, backend_file):
-        backend_file.write('SIMPLE_PROGRAMS += %s\n' % program)
+    def _process_simple_program(self, obj, backend_file):
+        if obj.is_unit_test:
+            backend_file.write('CPP_UNIT_TESTS += %s\n' % obj.program)
+        else:
+            backend_file.write('SIMPLE_PROGRAMS += %s\n' % obj.program)
 
     def _process_host_simple_program(self, program, backend_file):
         backend_file.write('HOST_SIMPLE_PROGRAMS += %s\n' % program)
 
     def _process_test_manifest(self, obj, backend_file):
         # Much of the logic in this function could be moved to CommonBackend.
         self.backend_input_files.add(mozpath.join(obj.topsrcdir,
             obj.manifest_relpath))
--- a/python/mozbuild/mozbuild/frontend/data.py
+++ b/python/mozbuild/mozbuild/frontend/data.py
@@ -357,23 +357,24 @@ class BaseProgram(Linkable):
     This class handles automatically appending a binary suffix to the program
     name.
     If the suffix is not defined, the program name is unchanged.
     Otherwise, if the program name ends with the given suffix, it is unchanged
     Otherwise, the suffix is appended to the program name.
     """
     __slots__ = ('program')
 
-    def __init__(self, sandbox, program):
+    def __init__(self, sandbox, program, is_unit_test=False):
         Linkable.__init__(self, sandbox)
 
         bin_suffix = sandbox['CONFIG'].get(self.SUFFIX_VAR, '')
         if not program.endswith(bin_suffix):
             program += bin_suffix
         self.program = program
+        self.is_unit_test = is_unit_test
 
 
 class Program(BaseProgram):
     """Sandbox container object for PROGRAM"""
     SUFFIX_VAR = 'BIN_SUFFIX'
     KIND = 'target'
 
 
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -319,17 +319,16 @@ class TreeMetadataEmitter(LoggingMixin):
 
         # 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(sandbox)
         varlist = [
             'ANDROID_GENERATED_RESFILES',
             'ANDROID_RES_DIRS',
-            'CPP_UNIT_TESTS',
             'DISABLE_STL_WRAPPING',
             'EXTRA_ASSEMBLER_FLAGS',
             'EXTRA_COMPILE_FLAGS',
             'EXTRA_COMPONENTS',
             'EXTRA_DSO_LDOPTS',
             'EXTRA_JS_MODULES',
             'EXTRA_PP_COMPONENTS',
             'EXTRA_PP_JS_MODULES',
@@ -444,26 +443,29 @@ class TreeMetadataEmitter(LoggingMixin):
                         'because it is already used in %s' % (program, kind,
                         self._binaries[program].relativedir), sandbox)
                 self._binaries[program] = cls(sandbox, program)
                 self._linkage.append((sandbox, self._binaries[program],
                     kind.replace('PROGRAM', 'USE_LIBS')))
 
         for kind, cls in [
                 ('SIMPLE_PROGRAMS', SimpleProgram),
+                ('CPP_UNIT_TESTS', SimpleProgram),
                 ('HOST_SIMPLE_PROGRAMS', HostSimpleProgram)]:
             for program in sandbox[kind]:
                 if program in self._binaries:
                     raise SandboxValidationError(
                         'Cannot use "%s" in %s, '
                         'because it is already used in %s' % (program, kind,
                         self._binaries[program].relativedir), sandbox)
-                self._binaries[program] = cls(sandbox, program)
+                self._binaries[program] = cls(sandbox, program,
+                    is_unit_test=kind == 'CPP_UNIT_TESTS')
                 self._linkage.append((sandbox, self._binaries[program],
-                    kind.replace('SIMPLE_PROGRAM', 'USE_LIBS')))
+                    'HOST_USE_LIBS' if kind == 'HOST_SIMPLE_PROGRAMS'
+                    else 'USE_LIBS'))
 
         test_js_modules = sandbox.get('TESTING_JS_MODULES')
         if test_js_modules:
             yield JavaScriptModules(sandbox, test_js_modules, 'testing')
 
         simple_lists = [
             ('GENERATED_EVENTS_WEBIDL_FILES', GeneratedEventWebIDLFile),
             ('GENERATED_WEBIDL_FILES', GeneratedWebIDLFile),
--- a/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
+++ b/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
@@ -229,20 +229,24 @@ VARIABLES = {
         """Library in which the objects of the current directory will be linked.
 
         This variable contains the name of a library, defined elsewhere with
         ``LIBRARY_NAME``, in which the objects of the current directory will be
         linked.
         """, 'binaries'),
 
     'CPP_UNIT_TESTS': (StrictOrderingOnAppendList, list,
-        """C++ source files for unit tests.
+        """Compile a list of C++ unit test names.
 
-        This is a list of C++ unit test sources. Entries must be files that
-        exist. These generally have ``.cpp`` extensions.
+        Each name in this variable corresponds to an executable built from the
+        corresponding source file with the same base name.
+
+        If the configuration token ``BIN_SUFFIX`` is set, its value will be
+        automatically appended to each name. If a name already ends with
+        ``BIN_SUFFIX``, the name will remain unchanged.
         """, 'binaries'),
 
     'FAIL_ON_WARNINGS': (bool, bool,
         """Whether to treat warnings as errors.
         """, None),
 
     'FORCE_SHARED_LIB': (bool, bool,
         """Whether the library in this directory is a shared library.
--- a/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build
+++ b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build
@@ -5,18 +5,16 @@
 SOURCES += ['bar.s', 'foo.asm']
 
 EXTRA_COMPONENTS = ['bar.js', 'foo.js']
 EXTRA_PP_COMPONENTS = ['bar.pp.js', 'foo.pp.js']
 
 EXTRA_JS_MODULES = ['bar.jsm', 'foo.jsm']
 EXTRA_PP_JS_MODULES = ['bar.pp.jsm', 'foo.pp.jsm']
 
-CPP_UNIT_TESTS = ['foo.cpp']
-
 HOST_SOURCES += ['bar.cpp', 'foo.cpp']
 HOST_SOURCES += ['bar.c', 'foo.c']
 
 OS_LIBS = ['foo.so', '-l123', 'bar.a']
 
 SOURCES += ['bar.c', 'foo.c']
 
 SOURCES += ['bar.mm', 'foo.mm']
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
+++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
@@ -257,19 +257,16 @@ class TestRecursiveMakeBackend(BackendTe
             'ASFILES': [
                 'ASFILES += bar.s',
                 'ASFILES += foo.asm',
             ],
             'CMMSRCS': [
                 'CMMSRCS += bar.mm',
                 'CMMSRCS += foo.mm',
             ],
-            'CPP_UNIT_TESTS': [
-                'CPP_UNIT_TESTS += foo.cpp',
-            ],
             'CSRCS': [
                 'CSRCS += bar.c',
                 'CSRCS += foo.c',
             ],
             'DISABLE_STL_WRAPPING': [
                 'DISABLE_STL_WRAPPING := 1',
             ],
             'EXTRA_COMPONENTS': [
--- a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build
+++ b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build
@@ -5,18 +5,16 @@
 SOURCES += ['fans.asm', 'tans.s']
 
 EXTRA_COMPONENTS=['fans.js', 'tans.js']
 EXTRA_PP_COMPONENTS=['fans.pp.js', 'tans.pp.js']
 
 EXTRA_JS_MODULES = ['bar.jsm', 'foo.jsm']
 EXTRA_PP_JS_MODULES = ['bar.pp.jsm', 'foo.pp.jsm']
 
-CPP_UNIT_TESTS = ['foo.cpp']
-
 HOST_SOURCES += ['fans.cpp', 'tans.cpp']
 HOST_SOURCES += ['fans.c', 'tans.c']
 
 OS_LIBS += ['foo.so', '-l123', 'aaa.a']
 
 SOURCES += ['fans.c', 'tans.c']
 
 SOURCES += ['fans.mm', 'tans.mm']
--- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py
@@ -153,17 +153,16 @@ class TestEmitterBasic(unittest.TestCase
 
         self.assertEqual(len(objs), 1)
         self.assertIsInstance(objs[0], VariablePassthru)
 
         wanted = dict(
             ASFILES=['fans.asm', 'tans.s'],
             CMMSRCS=['fans.mm', 'tans.mm'],
             CSRCS=['fans.c', 'tans.c'],
-            CPP_UNIT_TESTS=['foo.cpp'],
             DISABLE_STL_WRAPPING=True,
             EXTRA_COMPONENTS=['fans.js', 'tans.js'],
             EXTRA_PP_COMPONENTS=['fans.pp.js', 'tans.pp.js'],
             EXTRA_JS_MODULES=['bar.jsm', 'foo.jsm'],
             EXTRA_PP_JS_MODULES=['bar.pp.jsm', 'foo.pp.jsm'],
             FAIL_ON_WARNINGS=True,
             HOST_CPPSRCS=['fans.cpp', 'tans.cpp'],
             HOST_CSRCS=['fans.c', 'tans.c'],
--- a/security/manager/ssl/tests/compiled/moz.build
+++ b/security/manager/ssl/tests/compiled/moz.build
@@ -1,9 +1,11 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 CPP_UNIT_TESTS += [
-  'TestCertDB.cpp',
+  'TestCertDB',
 ]
+
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
--- a/startupcache/test/moz.build
+++ b/startupcache/test/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 CPP_UNIT_TESTS += [
-    'TestStartupCache.cpp',
+    'TestStartupCache',
 ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 EXTRA_COMPONENTS += [
     'TestStartupCacheTelemetry.js',
     'TestStartupCacheTelemetry.manifest',
 ]
--- a/storage/test/moz.build
+++ b/storage/test/moz.build
@@ -2,34 +2,36 @@
 # 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/.
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 
 CPP_UNIT_TESTS += [
-    'test_AsXXX_helpers.cpp',
-    'test_async_callbacks_with_spun_event_loops.cpp',
-    'test_asyncStatementExecution_transaction.cpp',
-    'test_binding_params.cpp',
-    'test_file_perms.cpp',
-    'test_mutex.cpp',
-    'test_service_init_background_thread.cpp',
-    'test_statement_scoper.cpp',
-    'test_StatementCache.cpp',
-    'test_transaction_helper.cpp',
-    'test_true_async.cpp',
-    'test_unlock_notify.cpp',
+    'test_AsXXX_helpers',
+    'test_async_callbacks_with_spun_event_loops',
+    'test_asyncStatementExecution_transaction',
+    'test_binding_params',
+    'test_file_perms',
+    'test_mutex',
+    'test_service_init_background_thread',
+    'test_statement_scoper',
+    'test_StatementCache',
+    'test_transaction_helper',
+    'test_true_async',
+    'test_unlock_notify',
 ]
 
 if CONFIG['MOZ_DEBUG'] and CONFIG['OS_ARCH'] not in ('WINNT', 'Darwin'):
     # FIXME bug 523392: test_deadlock_detector doesn't like Windows
     # FIXME bug 523378: also fails on OS X
     CPP_UNIT_TESTS += [
-        'test_deadlock_detector.cpp',
+        'test_deadlock_detector',
     ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 LOCAL_INCLUDES += [
     '../src',
 ]
 
 FAIL_ON_WARNINGS = True
--- a/toolkit/components/places/tests/cpp/moz.build
+++ b/toolkit/components/places/tests/cpp/moz.build
@@ -1,12 +1,14 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 CPP_UNIT_TESTS += [
-    'test_IHistory.cpp',
+    'test_IHistory',
 ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 FAIL_ON_WARNINGS = True
 
--- a/toolkit/components/url-classifier/tests/moz.build
+++ b/toolkit/components/url-classifier/tests/moz.build
@@ -10,10 +10,12 @@ XPCSHELL_TESTS_MANIFESTS += ['unit/xpcsh
 
 JAR_MANIFESTS += ['jar.mn']
 
 #DEFINES['MOZILLA_INTERNAL_API'] = True
 
 # XXX Get this to work in libxul builds.
 ## simple c++ tests (no xpcom)
 #CPP_UNIT_TESTS += [ \
-#    'TestUrlClassifierUtils.cpp', \
+#    'TestUrlClassifierUtils', \
 #]
+#
+#SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
--- a/widget/tests/moz.build
+++ b/widget/tests/moz.build
@@ -4,22 +4,24 @@
 # 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/.
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 MOCHITEST_MANIFESTS += ['mochitest.ini']
 MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
 
 CPP_UNIT_TESTS += [
-    'TestAppShellSteadyState.cpp',
+    'TestAppShellSteadyState',
 ]
 
 FAIL_ON_WARNINGS = True
 
 # if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
 #     if CONFIG['NS_ENABLE_TSF']:
 #         Test disabled because it uses the internal string APIs incorrectly
 #         (see bug 582863)
-#         CPP_UNIT_TESTS += ['TestWinTSF.cpp']
+#         CPP_UNIT_TESTS += ['TestWinTSF']
 #
 #     Test disabled because it requires the internal API. Re-enabling this test
 #     is bug 652123.
-#     CPP_UNIT_TESTS += ['TestChromeMargin.cpp']
+#     CPP_UNIT_TESTS += ['TestChromeMargin']
+
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
--- a/xpcom/tests/moz.build
+++ b/xpcom/tests/moz.build
@@ -50,74 +50,76 @@ if CONFIG['WRAP_STL_INCLUDES'] and not C
 
 SOURCES += [
     '%s.cpp' % s for s in sorted(SIMPLE_PROGRAMS)
 ]
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 
 CPP_UNIT_TESTS += [
-    'ShowAlignments.cpp',
-    'TestAutoPtr.cpp',
-    'TestAutoRef.cpp',
-    'TestCOMArray.cpp',
-    'TestCOMPtr.cpp',
-    'TestCOMPtrEq.cpp',
-    'TestDeque.cpp',
-    'TestFile.cpp',
-    'TestHashtables.cpp',
-    'TestID.cpp',
-    'TestObserverArray.cpp',
-    'TestObserverService.cpp',
-    'TestPipe.cpp',
-    'TestPLDHash.cpp',
-    'TestRefPtr.cpp',
-    'TestStringAPI.cpp',
-    'TestTArray.cpp',
-    'TestTextFormatter.cpp',
-    'TestThreadUtils.cpp'
+    'ShowAlignments',
+    'TestAutoPtr',
+    'TestAutoRef',
+    'TestCOMArray',
+    'TestCOMPtr',
+    'TestCOMPtrEq',
+    'TestDeque',
+    'TestFile',
+    'TestHashtables',
+    'TestID',
+    'TestObserverArray',
+    'TestObserverService',
+    'TestPipe',
+    'TestPLDHash',
+    'TestRefPtr',
+    'TestStringAPI',
+    'TestTArray',
+    'TestTextFormatter',
+    'TestThreadUtils'
 ]
 
 if CONFIG['MOZ_MEMORY']:
     CPP_UNIT_TESTS += [
-        'TestJemalloc.cpp',
+        'TestJemalloc',
     ]
 
 # XXX Make these tests work in libxul builds.
 #CPP_UNIT_TESTS += [
-#    'TestArray.cpp',
-#    'TestCRT.cpp',
-#    'TestEncoding.cpp',
-#    'TestExpirationTracker.cpp',
-#    'TestPipes.cpp',
-#    'TestPriorityQueue.cpp',
-#    'TestStorageStream.cpp',
-#    'TestStrings.cpp',
-#    'TestSynchronization.cpp',
-#    'TestTArray.cpp',
-#    'TestThreadPool.cpp',
-#    'TestThreads.cpp',
-#    'TestTimeStamp.cpp',
-#    'TestXPIDLString.cpp',
-#    'TestUTF.cpp',
-#    'TestAtoms.cpp',
+#    'TestArray',
+#    'TestCRT',
+#    'TestEncoding',
+#    'TestExpirationTracker',
+#    'TestPipes',
+#    'TestPriorityQueue',
+#    'TestStorageStream',
+#    'TestStrings',
+#    'TestSynchronization',
+#    'TestTArray',
+#    'TestThreadPool',
+#    'TestThreads',
+#    'TestTimeStamp',
+#    'TestXPIDLString',
+#    'TestUTF',
+#    'TestAtoms',
 #]
 
 # FIXME: bug 577500 TestStaticAtoms fails when run in dist/bin
 #CPP_UNIT_TESTS += [
-#    'TestStaticAtoms.cpp',
+#    'TestStaticAtoms',
 #]
 
 if CONFIG['MOZ_DEBUG'] and CONFIG['OS_ARCH'] not in ('WINNT', 'Darwin'):
     # FIXME bug 523392: TestDeadlockDetector doesn't like Windows
     # FIXME bug 523378: also fails on OS X
     CPP_UNIT_TESTS += [
-        'TestDeadlockDetector.cpp',
-        'TestDeadlockDetectorScalability.cpp',
+        'TestDeadlockDetector',
+        'TestDeadlockDetectorScalability',
     ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)
+
 LOCAL_INCLUDES += [
     '../ds',
 ]
 
 RESOURCE_FILES += [
     'test.properties',
 ]
--- a/xpcom/tests/windows/moz.build
+++ b/xpcom/tests/windows/moz.build
@@ -1,11 +1,12 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
 CPP_UNIT_TESTS += [
-    'TestCOM.cpp',
-    'TestNtPathToDosPath.cpp',
+    'TestCOM',
+    'TestNtPathToDosPath',
 ]
 
+SOURCES += sorted('%s.cpp' % t for t in CPP_UNIT_TESTS)