waf: persist implicit dependency information across clean

Implicit dependency scanning takes significant time and, since it doesn't
produce files, it's okay to keep the resulting information across clean
commands as long as the scanner is triggered again if there's need to. This
commit accomplishes that.

The advantage of this approach can be observed by the following timings when
building the group "bin":

 Method                                                         Time
 ------------------------------------------------------------------------
 Fully clean build                                              5m18.633s
 Clean build with scanning result persisted                     4m23.346s
 Clean build with ccache but non-persistent scan results        1m40.125s
 Clean build with scanning results persisted and with ccache      14.843s

While at it, move management of information persisted across clean commands to
a separate module.
This commit is contained in:
Gustavo Jose de Sousa 2016-08-23 14:49:51 -03:00 committed by Lucas De Marchi
parent 6abcf6150c
commit 31965689cb
2 changed files with 66 additions and 17 deletions

View File

@ -0,0 +1,59 @@
# Copyright (C) 2016 Intel Corporation. All rights reserved.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Module that changes Waf to keep persistent information across clean operations
in for performance improvement.
"""
from waflib import Build, Task
Build.SAVED_ATTRS.append('ap_persistent_task_sigs')
Build.SAVED_ATTRS.append('ap_persistent_imp_sigs')
Build.SAVED_ATTRS.append('ap_persistent_node_deps')
_original_signature = Task.Task.signature
_original_sig_implicit_deps = Task.Task.sig_implicit_deps
if hasattr(_original_sig_implicit_deps, '__func__'):
_original_sig_implicit_deps = _original_sig_implicit_deps.__func__
def _signature(self):
s = _original_signature(self)
real_fn = self.sig_implicit_deps.__func__
if not self.scan or _original_sig_implicit_deps != real_fn:
return s
bld = self.generator.bld
bld.ap_persistent_imp_sigs[self.uid()] = bld.imp_sigs[self.uid()]
bld.ap_persistent_node_deps[self.uid()] = bld.node_deps[self.uid()]
return s
Task.Task.signature = _signature
class CleanContext(Build.CleanContext):
def clean(self):
if not self.options.clean_all_sigs:
saved_task_sigs = dict(self.ap_persistent_task_sigs)
saved_imp_sigs = dict(self.ap_persistent_imp_sigs)
saved_node_deps = dict(self.ap_persistent_node_deps)
super(CleanContext, self).clean()
if not self.options.clean_all_sigs:
self.task_sigs.update(saved_task_sigs)
self.ap_persistent_task_sigs.update(saved_task_sigs)
self.imp_sigs.update(saved_imp_sigs)
self.ap_persistent_imp_sigs.update(saved_imp_sigs)
self.node_deps.update(saved_node_deps)
self.ap_persistent_node_deps.update(saved_node_deps)

View File

@ -8,6 +8,8 @@ from waflib.TaskGen import before_method, feature
import os.path, os import os.path, os
from collections import OrderedDict from collections import OrderedDict
import ap_persistent
SOURCE_EXTS = [ SOURCE_EXTS = [
'*.S', '*.S',
'*.c', '*.c',
@ -382,25 +384,13 @@ my board".
g.add_option('--clean-all-sigs', g.add_option('--clean-all-sigs',
action='store_true', action='store_true',
help=''' help='''
Clean signatures for all tasks. By default, some tasks that don't produce files Clean signatures for all tasks. By default, tasks that scan for implicit
and are time consuming keep their signatures when clean is called without this dependencies (like the compilation tasks) keep the dependency information
parameter. One example is the group of tasks that check headers included by across clean commands, so that that information is changed only when really
Ardupilot libraries. necessary. Also, some tasks that don't really produce files persist their
signature. This option avoids that behavior when cleaning the build.
''') ''')
def build(bld): def build(bld):
bld.add_pre_fun(_process_build_command) bld.add_pre_fun(_process_build_command)
bld.add_pre_fun(_select_programs_from_group) bld.add_pre_fun(_select_programs_from_group)
class CleanContext(Build.CleanContext):
Build.SAVED_ATTRS.append('ap_persistent_task_sigs')
def clean(self):
if not self.options.clean_all_sigs:
saved_sigs = dict(self.ap_persistent_task_sigs)
super(CleanContext, self).clean()
if not self.options.clean_all_sigs:
self.task_sigs.update(saved_sigs)
self.ap_persistent_task_sigs.update(saved_sigs)