Add unity block size option.

This commit is contained in:
Jussi Pakkanen 2019-12-21 21:22:45 +02:00
parent 6b5c1a4fc3
commit 47759550e5
5 changed files with 46 additions and 17 deletions

View File

@ -82,6 +82,7 @@ Using the option as-is with no prefix affects all machines. For example:
| stdsplit | true | Split stdout and stderr in test logs | no | | stdsplit | true | Split stdout and stderr in test logs | no |
| strip | false | Strip targets on install | no | | strip | false | Strip targets on install | no |
| unity {on, off, subprojects} | off | Unity build | no | | unity {on, off, subprojects} | off | Unity build | no |
| unity_size {>=2} | 4 | Unity file block size | no |
| warning_level {0, 1, 2, 3} | 1 | Set the warning level. From 0 = none to 3 = highest | no | | warning_level {0, 1, 2, 3} | 1 | Set the warning level. From 0 = none to 3 = highest | no |
| werror | false | Treat warnings as errors | no | | werror | false | Treat warnings as errors | no |
| wrap_mode {default, nofallback,<br>nodownload, forcefallback} | default | Wrap mode to use | no | | wrap_mode {default, nofallback,<br>nodownload, forcefallback} | default | Wrap mode to use | no |

View File

@ -0,0 +1,12 @@
## Unity file block size is configurable
Traditionally the unity files that Meson autogenerates contain all
source files that belong to a single target. This is the most
efficient setting for full builds but makes incremental builds slow.
This release adds a new option `unity_size` which specifies how many
source files should be put in each unity file.
The default value for block size is 4. This means that if you have a
target that has eight source files, Meson will generate two unity
files each of which includes four source files. The old behaviour can
be replicated by setting `unity_size` to a large value, such as 10000.

View File

@ -243,19 +243,20 @@ class Backend:
# target that the GeneratedList is used in # target that the GeneratedList is used in
return os.path.join(self.get_target_private_dir(target), src) return os.path.join(self.get_target_private_dir(target), src)
def get_unity_source_file(self, target, suffix): def get_unity_source_file(self, target, suffix, number):
# There is a potential conflict here, but it is unlikely that # There is a potential conflict here, but it is unlikely that
# anyone both enables unity builds and has a file called foo-unity.cpp. # anyone both enables unity builds and has a file called foo-unity.cpp.
osrc = target.name + '-unity.' + suffix osrc = '{}-unity{}.{}'.format(target.name, number, suffix)
return mesonlib.File.from_built_file(self.get_target_private_dir(target), osrc) return mesonlib.File.from_built_file(self.get_target_private_dir(target), osrc)
def generate_unity_files(self, target, unity_src): def generate_unity_files(self, target, unity_src):
abs_files = [] abs_files = []
result = [] result = []
compsrcs = classify_unity_sources(target.compilers.values(), unity_src) compsrcs = classify_unity_sources(target.compilers.values(), unity_src)
unity_size = self.get_option_for_target('unity_size', target)
def init_language_file(suffix): def init_language_file(suffix, unity_file_number):
unity_src = self.get_unity_source_file(target, suffix) unity_src = self.get_unity_source_file(target, suffix, unity_file_number)
outfileabs = unity_src.absolute_path(self.environment.get_source_dir(), outfileabs = unity_src.absolute_path(self.environment.get_source_dir(),
self.environment.get_build_dir()) self.environment.get_build_dir())
outfileabs_tmp = outfileabs + '.tmp' outfileabs_tmp = outfileabs + '.tmp'
@ -266,11 +267,23 @@ class Backend:
result.append(unity_src) result.append(unity_src)
return open(outfileabs_tmp, 'w') return open(outfileabs_tmp, 'w')
# For each language, generate a unity source file and return the list # For each language, generate unity source files and return the list
for comp, srcs in compsrcs.items(): for comp, srcs in compsrcs.items():
with init_language_file(comp.get_default_suffix()) as ofile: files_in_current = unity_size + 1
for src in srcs: unity_file_number = 0
ofile.write('#include<%s>\n' % src) ofile = None
for src in srcs:
if files_in_current >= unity_size:
if ofile:
ofile.close()
ofile = init_language_file(comp.get_default_suffix(), unity_file_number)
unity_file_number += 1
files_in_current = 0
ofile.write('#include<%s>\n' % src)
files_in_current += 1
if ofile:
ofile.close()
[mesonlib.replace_if_different(x, x + '.tmp') for x in abs_files] [mesonlib.replace_if_different(x, x + '.tmp') for x in abs_files]
return result return result
@ -489,16 +502,18 @@ class Backend:
targetdir = self.get_target_private_dir(extobj.target) targetdir = self.get_target_private_dir(extobj.target)
# With unity builds, there's just one object that contains all the # With unity builds, sources don't map directly to objects,
# sources, and we only support extracting all the objects in this mode, # we only support extracting all the objects in this mode,
# so just return that. # so just return all object files.
if self.is_unity(extobj.target): if self.is_unity(extobj.target):
compsrcs = classify_unity_sources(extobj.target.compilers.values(), sources) compsrcs = classify_unity_sources(extobj.target.compilers.values(), sources)
sources = [] sources = []
for comp in compsrcs.keys(): unity_size = self.get_option_for_target('unity_size', extobj.target)
osrc = self.get_unity_source_file(extobj.target, for comp, srcs in compsrcs.items():
comp.get_default_suffix()) for i in range(len(srcs) // unity_size + 1):
sources.append(osrc) osrc = self.get_unity_source_file(extobj.target,
comp.get_default_suffix(), i)
sources.append(osrc)
for osrc in sources: for osrc in sources:
objname = self.object_filename_from_source(extobj.target, osrc) objname = self.object_filename_from_source(extobj.target, osrc)

View File

@ -1067,6 +1067,7 @@ builtin_options = OrderedDict([
('stdsplit', BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True)), ('stdsplit', BuiltinOption(UserBooleanOption, 'Split stdout and stderr in test logs', True)),
('strip', BuiltinOption(UserBooleanOption, 'Strip targets on install', False)), ('strip', BuiltinOption(UserBooleanOption, 'Strip targets on install', False)),
('unity', BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects'])), ('unity', BuiltinOption(UserComboOption, 'Unity build', 'off', choices=['on', 'off', 'subprojects'])),
('unity_size', BuiltinOption(UserIntegerOption, 'Unity block size', (2, None, 4))),
('warning_level', BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3'])), ('warning_level', BuiltinOption(UserComboOption, 'Compiler warning level to use', '1', choices=['0', '1', '2', '3'])),
('werror', BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False)), ('werror', BuiltinOption(UserBooleanOption, 'Treat warnings as errors', False)),
('wrap_mode', BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback'])), ('wrap_mode', BuiltinOption(UserComboOption, 'Wrap mode', 'default', choices=['default', 'nofallback', 'nodownload', 'forcefallback'])),

View File

@ -5305,9 +5305,9 @@ class LinuxlikeTests(BasePlatformTests):
testdir = os.path.join(self.common_test_dir, '45 subproject') testdir = os.path.join(self.common_test_dir, '45 subproject')
self.init(testdir, extra_args='--unity=subprojects') self.init(testdir, extra_args='--unity=subprojects')
simpletest_id = Target.construct_id_from_path('subprojects/sublib', 'simpletest', '@exe') simpletest_id = Target.construct_id_from_path('subprojects/sublib', 'simpletest', '@exe')
self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib', simpletest_id, 'simpletest-unity.c')) self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib', simpletest_id, 'simpletest-unity0.c'))
sublib_id = Target.construct_id_from_path('subprojects/sublib', 'sublib', '@sha') sublib_id = Target.construct_id_from_path('subprojects/sublib', 'sublib', '@sha')
self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib', sublib_id, 'sublib-unity.c')) self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib', sublib_id, 'sublib-unity0.c'))
self.assertPathDoesNotExist(os.path.join(self.builddir, 'user@exe/user-unity.c')) self.assertPathDoesNotExist(os.path.join(self.builddir, 'user@exe/user-unity.c'))
self.build() self.build()