Skip to content

Commit

Permalink
Implement exclusion of sources and targets in VS backends.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vslavik committed Jun 7, 2013
1 parent 24a3d44 commit 45b8dba
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 22 deletions.
4 changes: 4 additions & 0 deletions src/bkl/compilers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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)

Expand Down
26 changes: 12 additions & 14 deletions src/bkl/interpreter/passes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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]
Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/bkl/makefile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down
41 changes: 41 additions & 0 deletions src/bkl/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand Down Expand Up @@ -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())

Expand Down Expand Up @@ -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):
Expand Down
5 changes: 4 additions & 1 deletion src/bkl/plugins/vs200x.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/bkl/plugins/vs201x.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
13 changes: 10 additions & 3 deletions src/bkl/plugins/vsbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []

Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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(),
Expand All @@ -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):
Expand Down
8 changes: 6 additions & 2 deletions tests/projects/conditional/conditional.bkl
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
toolsets = gnu gnu-osx vs2010;
toolsets = gnu gnu-osx vs2010 vs2008;

vs2008.solutionfile = conditional_2008.sln;

build_something = true;

Expand Down Expand Up @@ -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 }
}
}

Expand Down
6 changes: 4 additions & 2 deletions tests/projects/conditional/conditional.model
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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) }
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions tests/projects/conditional/debug.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

void debug_func()
{
}

0 comments on commit 45b8dba

Please sign in to comment.