From 45b8dbaceaae1889e87905f90056d56c33a18f22 Mon Sep 17 00:00:00 2001 From: Vaclav Slavik Date: Fri, 7 Jun 2013 16:45:14 +0200 Subject: [PATCH] Implement exclusion of sources and targets in VS backends. Support generating Visual Studio projects with files excluded from the build in some configurations. Also implement exclusion of whole targets/projects from the solution. To use, just enclose the sources {} statement or the target inside an if block that tests the value of $(config). Updated tests/projects/conditional to demonstrate and test this. --- src/bkl/compilers.py | 4 ++ src/bkl/interpreter/passes.py | 26 ++++++------- src/bkl/makefile.py | 2 + src/bkl/model.py | 41 ++++++++++++++++++++ src/bkl/plugins/vs200x.py | 5 ++- src/bkl/plugins/vs201x.py | 2 + src/bkl/plugins/vsbase.py | 13 +++++-- tests/projects/conditional/conditional.bkl | 8 +++- tests/projects/conditional/conditional.model | 6 ++- tests/projects/conditional/debug.c | 4 ++ 10 files changed, 89 insertions(+), 22 deletions(-) create mode 100644 tests/projects/conditional/debug.c diff --git a/src/bkl/compilers.py b/src/bkl/compilers.py index d3b60358..bf53aec4 100644 --- a/src/bkl/compilers.py +++ b/src/bkl/compilers.py @@ -231,6 +231,8 @@ def get_compilation_subgraph(toolset, target, ft_to, outfile): for srcfile in target.sources: with error_context(srcfile): + if not srcfile.should_build(): # TODO: allow runtime decision + continue if srcfile["compile-commands"]: allnodes += _make_build_nodes_for_generated_file(srcfile) else: @@ -240,6 +242,8 @@ def get_compilation_subgraph(toolset, target, ft_to, outfile): allnodes += all for srcfile in target.headers: with error_context(srcfile): + if not srcfile.should_build(): # TODO: allow runtime decision + continue if srcfile["compile-commands"]: allnodes += _make_build_nodes_for_generated_file(srcfile) diff --git a/src/bkl/interpreter/passes.py b/src/bkl/interpreter/passes.py index b8d2d33c..9b28d8ff 100644 --- a/src/bkl/interpreter/passes.py +++ b/src/bkl/interpreter/passes.py @@ -97,21 +97,19 @@ def remove_disabled_model_parts(model, toolset): are those with ``condition`` variable evaluating to false. """ - def _should_remove(part): - if part.condition is None: - return False + def _should_remove(part, allow_dynamic): try: - return not part.condition.as_py() + return not part.should_build() except NonConstError: - cond = simplify.simplify(part.condition) - raise Error("condition for building %s couldn't be resolved\n(condition \"%s\" set at %s)" % - (part, cond, cond.pos), - pos=part.source_pos) + if allow_dynamic: + return False + else: + raise - def _remove_from_list(parts): + def _remove_from_list(parts, allow_dynamic): to_del = [] for p in parts: - if _should_remove(p): + if _should_remove(p, allow_dynamic): to_del.append(p) for p in to_del: logger.debug("removing disabled %s from %s", p, p.parent) @@ -120,11 +118,11 @@ def _remove_from_list(parts): for module in model.modules: targets_to_del = [] for target in module.targets.itervalues(): - if _should_remove(target): + if _should_remove(target, allow_dynamic=True): targets_to_del.append(target) continue - _remove_from_list(target.sources) - _remove_from_list(target.headers) + _remove_from_list(target.sources, allow_dynamic=True) + _remove_from_list(target.headers, allow_dynamic=True) for target in targets_to_del: logger.debug("removing disabled %s", target) del module.targets[target.name] @@ -149,7 +147,7 @@ def _remove_from_list(parts): # and remove unused settings too: settings_to_del = [] for sname, setting in model.settings.iteritems(): - if _should_remove(setting): + if _should_remove(setting, allow_dynamic=False): settings_to_del.append(sname) for sname in settings_to_del: logger.debug("removing setting %s", sname) diff --git a/src/bkl/makefile.py b/src/bkl/makefile.py index c3acd65f..c043c174 100644 --- a/src/bkl/makefile.py +++ b/src/bkl/makefile.py @@ -182,6 +182,8 @@ def generate(self, project): norm = PathsNormalizer(project) for t in project.all_targets(): with error_context(t): + if not t.should_build(): + continue norm.set_context(t) graph = t.type.get_build_subgraph(self, t) for node in graph.all_nodes(): diff --git a/src/bkl/model.py b/src/bkl/model.py index b8f26fd9..04fc0139 100644 --- a/src/bkl/model.py +++ b/src/bkl/model.py @@ -291,6 +291,25 @@ def condition(self): except KeyError: return None + def should_build(self): + """ + Evaluates the `condition` property on this part and returns True if it + should be built or False otherwise. Throws NonConstError if it cannot + be determined. + """ + # see also ConfigurationProxy.should_build(), keep in sync! + cond = self.condition + if cond is None: + return True + try: + return cond.as_py() + except error.NonConstError: + from bkl.interpreter.simplify import simplify + cond = simplify(cond) + raise error.CannotDetermineError("condition for building %s couldn't be resolved\n(condition \"%s\" set at %s)" % + (self, cond, cond.pos), + pos=self.source_pos) + def get_variable(self, name): """ @@ -757,6 +776,13 @@ def __init__(self, config): self.mapping = {"config": config} self.inside_cond = 0 + def visit_cond(self, e): + try: + self.inside_cond += 1 + return self.visit(e) + finally: + self.inside_cond -= 1 + def reference(self, e): return self.visit(e.get_value()) @@ -816,6 +842,21 @@ def apply_subst(self, value): else: return self._visitor.visit(value) + def should_build(self): + # see ModelPart.should_build() + cond = self.model.condition + if cond is None: + return True + cond = self._visitor.visit_cond(cond) + try: + return cond.as_py() + except error.NonConstError: + from bkl.interpreter.simplify import simplify + cond = simplify(cond) + raise error.CannotDetermineError("condition for building %s couldn't be resolved\n(condition \"%s\" set at %s)" % + (self.model, cond, cond.pos), + pos=self.model.source_pos) + class Target(ModelPart, ConfigurationsPropertyMixin): diff --git a/src/bkl/plugins/vs200x.py b/src/bkl/plugins/vs200x.py index 667849c2..ab545d40 100644 --- a/src/bkl/plugins/vs200x.py +++ b/src/bkl/plugins/vs200x.py @@ -549,11 +549,14 @@ def _add_per_file_options(self, srcfile, node, tool, additional_options): # TODO: add regular options such as 'defines' here too, not just # the vsXXXX.option.* overrides for cfg in self.configs_and_platforms(srcfile): + excluded = not cfg.should_build() extras = list(self.collect_extra_options_for_node(srcfile, tool, inherit=False)) if additional_options: extras = additional_options + extras - if extras: + if extras or excluded: n_cfg = Node("FileConfiguration", Name="%s" % cfg.vs_name) + if excluded: + n_cfg["ExcludedFromBuild"] = True n_tool = Node("Tool", Name=tool) for key, value in extras: n_tool[key] = value diff --git a/src/bkl/plugins/vs201x.py b/src/bkl/plugins/vs201x.py index c3ad8391..90e1b478 100644 --- a/src/bkl/plugins/vs201x.py +++ b/src/bkl/plugins/vs201x.py @@ -411,6 +411,8 @@ def _add_per_file_options(self, srcfile, node): # the vsXXXX.option.* overrides for cfg in self.configs_and_platforms(srcfile): cond = "'$(Configuration)|$(Platform)'=='%s'" % cfg.vs_name + if not cfg.should_build(): + node.add(Node("ExcludedFromBuild", True, Condition=cond)) for key, value in self.collect_extra_options_for_node(srcfile, node.name, inherit=False): node.add(Node(key, value, Condition=cond)) diff --git a/src/bkl/plugins/vsbase.py b/src/bkl/plugins/vsbase.py index 640c5093..03187dee 100644 --- a/src/bkl/plugins/vsbase.py +++ b/src/bkl/plugins/vsbase.py @@ -348,6 +348,9 @@ class VSProjectBase(object): #: List of configuration objects.""" configurations = [] + #: List of configurations objects for which building the project is disabled. + disabled_configurations = [] + #: List of platforms ("Win32", "x64").""" platforms = [] @@ -564,7 +567,8 @@ class AdditionalDepsFolder: pass outf.write("\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n" % (guid, cfg.name, plat, cfgp.name, platp)) else: outf.write("\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n" % (guid, cfg.name, plat, cfgp.name, platp)) - outf.write("\t\t{%s}.%s|%s.Build.0 = %s|%s\n" % (guid, cfg.name, plat, cfgp.name, platp)) + if cfgp not in prj.disabled_configurations: + outf.write("\t\t{%s}.%s|%s.Build.0 = %s|%s\n" % (guid, cfg.name, plat, cfgp.name, platp)) outf.write("\tEndGlobalSection\n") outf.write("\tGlobalSection(SolutionProperties) = preSolution\n") outf.write("\t\tHideSolutionNode = FALSE\n") @@ -730,7 +734,7 @@ def get_project_paths_info(self, target, project): @memoized def get_project_object(self, target): if self.is_natively_supported(target): - return self.Project(target.name, + proj = self.Project(target.name, target["%s.guid" % self.name].as_py(), target["%s.projectfile" % self.name], target["deps"].as_py(), @@ -740,12 +744,15 @@ def get_project_object(self, target): else: # Not natively supported; try if the TargetType has an implementation try: - return target.type.vs_project(self, target) + proj = target.type.vs_project(self, target) except NotImplementedError: # TODO: handle this as generic action target warning("target type \"%s\" is not supported by the %s toolset, ignoring", target.type.name, self.name) return None + proj.disabled_configurations = [x.config for x in target.configurations + if not x.should_build()] + return proj def get_builddir_for(self, target): diff --git a/tests/projects/conditional/conditional.bkl b/tests/projects/conditional/conditional.bkl index 271bf80c..9b00341f 100644 --- a/tests/projects/conditional/conditional.bkl +++ b/tests/projects/conditional/conditional.bkl @@ -1,4 +1,6 @@ -toolsets = gnu gnu-osx vs2010; +toolsets = gnu gnu-osx vs2010 vs2008; + +vs2008.solutionfile = conditional_2008.sln; build_something = true; @@ -26,9 +28,11 @@ if ( $(build_something) ) { } } - if ( $(toolset) == vs2010 ) { + if ( $(toolset) == vs2010 || $(toolset) == vs2008 ) { program hello_windows { sources { hello.c } + if ( $config == Debug ) + sources { debug.c } } } diff --git a/tests/projects/conditional/conditional.model b/tests/projects/conditional/conditional.model index ead2c15c..47352759 100644 --- a/tests/projects/conditional/conditional.model +++ b/tests/projects/conditional/conditional.model @@ -1,6 +1,7 @@ module { variables { - toolsets = [gnu, gnu-osx, vs2010] + toolsets = [gnu, gnu-osx, vs2010, vs2008] + vs2008.solutionfile = @top_srcdir/conditional_2008.sln build_something = true } targets { @@ -19,9 +20,10 @@ module { } } program hello_windows { - _condition = (true && ($(toolset) == vs2010)) + _condition = (true && (($(toolset) == vs2010) || ($(toolset) == vs2008))) sources { file @top_srcdir/hello.c + file @top_srcdir/debug.c { _condition = ($(config) == Debug) } } } } diff --git a/tests/projects/conditional/debug.c b/tests/projects/conditional/debug.c new file mode 100644 index 00000000..91e6fd91 --- /dev/null +++ b/tests/projects/conditional/debug.c @@ -0,0 +1,4 @@ + +void debug_func() +{ +}