def __init__(self, toolset, module): slnfile = module["%s.solutionfile" % toolset.name].as_native_path_for_output(module) self.name = module.name # unlike targets, modules' names aren't globally unique, so use the fully qualified name, which is self.guid = GUID(NAMESPACE_SLN_GROUP, module.project.top_module.name, module.fully_qualified_name) self.projects = OrderedDict() self.subsolutions = [] self.parent_solution = None paths_info = bkl.expr.PathAnchorsInfo( dirsep="\\", outfile=slnfile, builddir=None, model=module) self.formatter = VSExprFormatter(module.project.settings, paths_info) self.generate_outf = module["%s.generate-solution" % toolset.name] if self.generate_outf: self.outf = OutputFile(slnfile, EOL_WINDOWS, creator=toolset, create_for=module) else: self.outf = None
def write(self): """Writes the solution to the file.""" if not self.generate_outf: return # silently do nothing outf = self.outf self.write_header(outf) # Projects: all_own_projects = list(self.all_projects()) additional_deps = self.additional_deps() included_projects = all_own_projects + additional_deps if not included_projects: return # don't write empty solution files configurations = OrderedSet() for prj in all_own_projects: configurations.update(prj.configurations) platforms = OrderedSet() for prj in all_own_projects: platforms.update(prj.platforms) # HACK: Don't use Any CPU for solution config if there are native ones: if "Any CPU" in platforms and len(platforms) > 1: platforms.remove("Any CPU") for prj in included_projects: outf.write('Project("%s") = "%s", "%s", "{%s}"\n' % (prj.kind, prj.name, self.formatter.format(prj.projectfile), str(prj.guid))) if prj.dependencies: outf.write("\tProjectSection(ProjectDependencies) = postProject\n") for d in prj.dependencies: outf.write("\t\t{%(g)s} = {%(g)s}\n" % {'g':self._get_target_guid(d)}) outf.write("\tEndProjectSection\n") outf.write("EndProject\n") # Folders in the solution: all_folders = list(self.all_subsolutions()) if additional_deps: class AdditionalDepsFolder: pass extras = AdditionalDepsFolder() extras.name = "Additional Dependencies" extras.guid = GUID(NAMESPACE_INTERNAL, self.name, extras.name) extras.projects = OrderedDict() for prj in additional_deps: extras.projects[prj.name] = prj extras.subsolutions = [] extras.parent_solution = None all_folders.append(extras) for sln in all_folders: # don't have folders with just one item in them: sln.omit_from_tree = (sln.parent_solution and (len(sln.projects) + len(sln.subsolutions)) <= 1) if sln.omit_from_tree: continue outf.write('Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "%s", "%s", "{%s}"\n' % (sln.name, sln.name, sln.guid)) outf.write("EndProject\n") all_folders = list(x for x in all_folders if not x.omit_from_tree) # Global settings: outf.write("Global\n") outf.write("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n") for cfg in configurations: for plat in platforms: outf.write("\t\t%s|%s = %s|%s\n" % (cfg.name, plat, cfg.name, plat)) outf.write("\tEndGlobalSection\n") outf.write("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n") for prj in included_projects: guid = prj.guid for cfg in configurations: cfgp = _get_matching_project_config(cfg, prj) for plat in platforms: platp = _get_matching_project_platform(plat, prj) if platp is None: # Can't build in this solution config. Just use any project platform # and omit the Build.0 node -- VS does the same in this case. platp = prj.platforms[0] 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)) if cfg 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") outf.write("\tEndGlobalSection\n") # Nesting of projects and folders in the tree: if all_folders: outf.write("\tGlobalSection(NestedProjects) = preSolution\n") def _gather_folder_children(sln): prjs = [p for p in sln.projects.itervalues()] slns = [] for s in sln.subsolutions: if s.omit_from_tree: p2, s2 = _gather_folder_children(s) prjs += p2 slns += s2 else: slns.append(s) return (prjs, slns) for sln in all_folders: prjs, subslns = _gather_folder_children(sln) for prj in prjs: outf.write("\t\t{%s} = {%s}\n" % (prj.guid, sln.guid)) for subsln in subslns: outf.write("\t\t{%s} = {%s}\n" % (subsln.guid, sln.guid)) outf.write("\tEndGlobalSection\n") outf.write("EndGlobal\n") outf.commit()
def _gen_makefile(self, build_graphs, module): output_value = module.get_variable_value("%s.makefile" % self.name) output = output_value.as_native_path_for_output(module) paths_info = expr.PathAnchorsInfo( dirsep="/", # FIXME - format-configurable outfile=output, builddir=self.get_module_builddir(module).as_native_path_for_output(module), model=module) mk_fmt = self.Formatter() expr_fmt = self.ExprFormatter(self, paths_info) f = io.OutputFile(output, io.EOL_UNIX, creator=self, create_for=module) self.on_header(f, module) f.write(""" # The directory for the build files, may be overridden on make command line. builddir = . """) self._gen_settings(module, mk_fmt, expr_fmt, f) #FIXME: make this part of the formatter for (future) IdRefExpr def _format_dep(t): g = build_graphs[t].main if len(g.outputs) == 0: assert g.name if t.parent is not module: raise Error("cross-module dependencies on phony targets (\"%s\") not supported yet" % t.name) # TODO out = g.name else: # FIXME: handle multi-output nodes too assert len(g.outputs) == 1 out = g.outputs[0] return expr_fmt.format(out) def _get_submodule_deps(main, submodule): """ Return list of dependencies that 'submodule' has on other submodules of 'main'. Submodules have dependency if a target from one depends on a target from another. """ mod_deps = set() project = main.project inspect = [submodule] + [p for p in project.modules if p.is_submodule_of(submodule)] for mod in inspect: for target in mod.targets.itervalues(): for dep in target["deps"]: tdep = project.get_target(dep.as_py()) tmod = tdep.parent if tmod is main: mod_deps.add(_format_dep(tdep)) elif tmod.is_submodule_of(main): while tmod.parent is not main: tmod = tmod.parent if tmod is not submodule: mod_deps.add(tmod.name) return sorted(mod_deps) # Write the "all" target: all_targets = ( [_format_dep(t) for t in module.targets.itervalues()] + [sub.name for sub in module.submodules] ) f.write(mk_fmt.target(name="all", deps=all_targets, commands=None)) phony_targets = ["all", "clean"] targets_from_submodules = OrderedDict() submakefiles = OrderedDict() for sub in module.submodules: subpath = sub.get_variable_value("%s.makefile" % self.name) # FIXME: use $dirname(), $basename() functions, this is hacky subdir = subpath.get_directory_path() subfile = subpath.components[-1] submakefiles[sub] = (sub.name, expr_fmt.format(subdir), expr_fmt.format(subfile), _get_submodule_deps(module, sub)) for subname, subdir, subfile, subdeps in submakefiles.itervalues(): subcmd = mk_fmt.submake_command(subdir, subfile, "all") f.write(mk_fmt.target(name=subname, deps=subdeps, commands=[subcmd])) phony_targets.append(subname) for t in module.targets.itervalues(): with error_context(t): # collect target's dependencies target_deps = [] for dep in t["deps"]: tdep = module.project.get_target(dep.as_py()) tdepstr = _format_dep(tdep) target_deps.append(tdepstr) if tdep.parent is not module: # link external dependencies with submodules to build them tmod = tdep.parent while tmod.parent is not None and tmod.parent is not module: tmod = tmod.parent if tmod in module.submodules: targets_from_submodules[tdepstr] = tmod # generate code for the target's build graph: graph = build_graphs[t] for node in graph.all_nodes(): with error_context(node): if node.outputs: out = node.outputs else: out = [node.name] phony_targets.append(expr_fmt.format(out[0])) deps = [expr_fmt.format(i) for i in node.inputs] if node is graph.main: deps += target_deps out_fmt = [expr_fmt.format(x) for x in out] commands_fmt = [expr_fmt.format(c) for c in node.commands] if len(out_fmt) == 1: text = mk_fmt.target(name=out_fmt[0], deps=deps, commands=commands_fmt) else: text = mk_fmt.multifile_target( outfiles=out_fmt, deps=deps, commands=commands_fmt) f.write(text) all_targets += out_fmt # dependencies on submodules to build targets from them: if targets_from_submodules: f.write("# Targets from sub-makefiles:\n") for t, tsub in targets_from_submodules.iteritems(): f.write(mk_fmt.target(name=t, deps=[submakefiles[tsub][0]], commands=None)) # Write the "clean" target: clean_cmds = self._get_clean_commands( mk_fmt, expr_fmt, (build_graphs[t] for t in module.targets.itervalues()), submakefiles.itervalues()) f.write(mk_fmt.target(name="clean", deps=[], commands=clean_cmds)) self.on_phony_targets(f, phony_targets) self.on_footer(f, module) f.commit()
class VSSolutionBase(object): """ Base class for a representation of a Visual Studio solution file. Derived classes must set :attr:`format_version` and :attr:`human_version` and may override :meth:`write_header()`. """ #: String with format version as used in the header format_version = None #: ...and in the comment under it (2005 and up) human_version = None def __init__(self, toolset, module): slnfile = module["%s.solutionfile" % toolset.name].as_native_path_for_output(module) self.name = module.name # unlike targets, modules' names aren't globally unique, so use the fully qualified name, which is self.guid = GUID(NAMESPACE_SLN_GROUP, module.project.top_module.name, module.fully_qualified_name) self.projects = OrderedDict() self.subsolutions = [] self.parent_solution = None paths_info = bkl.expr.PathAnchorsInfo( dirsep="\\", outfile=slnfile, builddir=None, model=module) self.formatter = VSExprFormatter(module.project.settings, paths_info) self.generate_outf = module["%s.generate-solution" % toolset.name] if self.generate_outf: self.outf = OutputFile(slnfile, EOL_WINDOWS, creator=toolset, create_for=module) else: self.outf = None def add_project(self, prj): """ Adds a project (VSProjectBase-derived object) to the solution. """ self.projects[prj.name] = prj def add_subsolution(self, solution): """ Adds another solution as a subsolution of this one. """ assert self.format_version == solution.format_version self.subsolutions.append(solution) solution.parent_solution = self def all_projects(self): for p in self.projects.itervalues(): yield p for sln in self.subsolutions: for p in sln.all_projects(): yield p def all_subsolutions(self): for sln in self.subsolutions: yield sln for s in sln.all_subsolutions(): yield s def _get_project_by_id(self, id): try: return self.projects[id] except KeyError: for sln in self.subsolutions: p = sln._get_project_by_id(id) if p: return p return None def additional_deps(self): """ Returns additional projects to include, "external" deps e.g. from parents, in the same format all_projects() uses. """ additional = [] top = self while top.parent_solution: top = top.parent_solution if top is self: return additional included = set(x.name for x in self.all_projects()) todo = set() for prj in self.all_projects(): if prj.dependencies: todo.update(prj.dependencies) prev_count = 0 while prev_count != len(included): prev_count = len(included) todo = set(x for x in todo if x not in included) todo_new = set() for todo_item in sorted(todo): included.add(todo_item) prj = top._get_project_by_id(todo_item) if prj.dependencies: todo_new.update(prj.dependencies) additional.append(prj) todo.update(todo_new) return additional def _find_target_guid_recursively(self, id): """Recursively search for the target in all submodules and return its GUID.""" try: return self.projects[id].guid except KeyError: for sln in self.subsolutions: guid = sln._find_target_guid_recursively(id) if guid: return guid return None def _get_target_guid(self, id): try: return self.projects[id].guid except KeyError: top = self while top.parent_solution: top = top.parent_solution guid = top._find_target_guid_recursively(id) assert guid, "can't find GUID of project '%s'" % id return guid def write_header(self, file): file.write("Microsoft Visual Studio Solution File, Format Version %s\n" % self.format_version) if self.human_version: file.write("# Visual Studio %s\n" % self.human_version) def write(self): """Writes the solution to the file.""" if not self.generate_outf: return # silently do nothing outf = self.outf self.write_header(outf) # Projects: all_own_projects = list(self.all_projects()) additional_deps = self.additional_deps() included_projects = all_own_projects + additional_deps if not included_projects: return # don't write empty solution files configurations = OrderedSet() for prj in all_own_projects: configurations.update(prj.configurations) platforms = OrderedSet() for prj in all_own_projects: platforms.update(prj.platforms) # HACK: Don't use Any CPU for solution config if there are native ones: if "Any CPU" in platforms and len(platforms) > 1: platforms.remove("Any CPU") for prj in included_projects: outf.write('Project("%s") = "%s", "%s", "{%s}"\n' % (prj.kind, prj.name, self.formatter.format(prj.projectfile), str(prj.guid))) if prj.dependencies: outf.write("\tProjectSection(ProjectDependencies) = postProject\n") for d in prj.dependencies: outf.write("\t\t{%(g)s} = {%(g)s}\n" % {'g':self._get_target_guid(d)}) outf.write("\tEndProjectSection\n") outf.write("EndProject\n") # Folders in the solution: all_folders = list(self.all_subsolutions()) if additional_deps: class AdditionalDepsFolder: pass extras = AdditionalDepsFolder() extras.name = "Additional Dependencies" extras.guid = GUID(NAMESPACE_INTERNAL, self.name, extras.name) extras.projects = OrderedDict() for prj in additional_deps: extras.projects[prj.name] = prj extras.subsolutions = [] extras.parent_solution = None all_folders.append(extras) for sln in all_folders: # don't have folders with just one item in them: sln.omit_from_tree = (sln.parent_solution and (len(sln.projects) + len(sln.subsolutions)) <= 1) if sln.omit_from_tree: continue outf.write('Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "%s", "%s", "{%s}"\n' % (sln.name, sln.name, sln.guid)) outf.write("EndProject\n") all_folders = list(x for x in all_folders if not x.omit_from_tree) # Global settings: outf.write("Global\n") outf.write("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n") for cfg in configurations: for plat in platforms: outf.write("\t\t%s|%s = %s|%s\n" % (cfg.name, plat, cfg.name, plat)) outf.write("\tEndGlobalSection\n") outf.write("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n") for prj in included_projects: guid = prj.guid for cfg in configurations: cfgp = _get_matching_project_config(cfg, prj) for plat in platforms: platp = _get_matching_project_platform(plat, prj) if platp is None: # Can't build in this solution config. Just use any project platform # and omit the Build.0 node -- VS does the same in this case. platp = prj.platforms[0] 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)) if cfg 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") outf.write("\tEndGlobalSection\n") # Nesting of projects and folders in the tree: if all_folders: outf.write("\tGlobalSection(NestedProjects) = preSolution\n") def _gather_folder_children(sln): prjs = [p for p in sln.projects.itervalues()] slns = [] for s in sln.subsolutions: if s.omit_from_tree: p2, s2 = _gather_folder_children(s) prjs += p2 slns += s2 else: slns.append(s) return (prjs, slns) for sln in all_folders: prjs, subslns = _gather_folder_children(sln) for prj in prjs: outf.write("\t\t{%s} = {%s}\n" % (prj.guid, sln.guid)) for subsln in subslns: outf.write("\t\t{%s} = {%s}\n" % (subsln.guid, sln.guid)) outf.write("\tEndGlobalSection\n") outf.write("EndGlobal\n") outf.commit()
def _gen_makefile(self, build_graphs, module): # Flag indicating whether this makefile actually builds anything. self.uses_builddir = False output_value = module.get_variable_value("%s.makefile" % self.name) output = output_value.as_native_path_for_output(module) paths_info = expr.PathAnchorsInfo( dirsep="/", # FIXME - format-configurable outfile=output, builddir=None, model=module) mk_fmt = self.Formatter() expr_fmt = self.ExprFormatter(self, paths_info) f = io.OutputFile(output, io.EOL_UNIX, creator=self, create_for=module) self.on_header(f, module) self._gen_settings(module, mk_fmt, expr_fmt, f) #FIXME: make this part of the formatter for (future) IdRefExpr def _format_dep(t): g = build_graphs[t].main if len(g.outputs) == 0: assert g.name if t.parent is not module: raise Error("cross-module dependencies on phony targets (\"%s\") not supported yet" % t.name) # TODO out = g.name else: # FIXME: handle multi-output nodes too assert len(g.outputs) == 1 out = g.outputs[0] return expr_fmt.format(out) def _get_submodule_deps(main, submodule): """ Return list of dependencies that 'submodule' has on other submodules of 'main'. Submodules have dependency if a target from one depends on a target from another. """ mod_deps = set() project = main.project inspect = [submodule] + [p for p in project.modules if p.is_submodule_of(submodule)] for mod in inspect: for target in mod.targets.itervalues(): for dep in target["deps"]: tdep = project.get_target(dep.as_py()) tmod = tdep.parent if tmod is main: mod_deps.add(_format_dep(tdep)) elif tmod.is_submodule_of(main): while tmod.parent is not main: tmod = tmod.parent if tmod is not submodule: mod_deps.add(tmod.name) return sorted(mod_deps) # Write the "all" target: all_targets = ( [_format_dep(t) for t in module.targets.itervalues()] + [sub.name for sub in module.submodules] ) f.write(mk_fmt.target(name="all", deps=all_targets, commands=None)) phony_targets = ["all", "clean"] targets_from_submodules = OrderedDict() submakefiles = OrderedDict() for sub in module.submodules: subpath = sub.get_variable_value("%s.makefile" % self.name) # FIXME: use $dirname(), $basename() functions, this is hacky subdir = subpath.get_directory_path() subfile = subpath.components[-1] submakefiles[sub] = (sub.name, expr_fmt.format(subdir), expr_fmt.format(subfile), _get_submodule_deps(module, sub)) for subname, subdir, subfile, subdeps in submakefiles.itervalues(): subcmd = mk_fmt.submake_command(subdir, subfile, "all") f.write(mk_fmt.target(name=subname, deps=subdeps, commands=[subcmd])) phony_targets.append(subname) for t in module.targets.itervalues(): with error_context(t): # collect target's dependencies target_deps = [] for dep in t["deps"]: tdep = module.project.get_target(dep.as_py()) tdepstr = _format_dep(tdep) target_deps.append(tdepstr) if tdep.parent is not module: # link external dependencies with submodules to build them tmod = tdep.parent while tmod.parent is not None and tmod.parent is not module: tmod = tmod.parent if tmod in module.submodules: targets_from_submodules[tdepstr] = tmod # generate code for the target's build graph: graph = build_graphs[t] for node in graph.all_nodes(): with error_context(node): if node.outputs: out = node.outputs else: out = [node.name] phony_targets.append(expr_fmt.format(out[0])) deps = [expr_fmt.format(i) for i in node.inputs] if node is graph.main: deps += target_deps out_fmt = [expr_fmt.format(x) for x in out] commands_fmt = [expr_fmt.format(c) for c in node.commands] if len(out_fmt) == 1: text = mk_fmt.target(name=out_fmt[0], deps=deps, commands=commands_fmt) else: text = mk_fmt.multifile_target( outputs=out, outfiles=out_fmt, deps=deps, commands=commands_fmt) f.write(text) all_targets += out_fmt # dependencies on submodules to build targets from them: if targets_from_submodules: f.write("# Targets from sub-makefiles:\n") for t, tsub in targets_from_submodules.iteritems(): f.write(mk_fmt.target(name=t, deps=[submakefiles[tsub][0]], commands=None)) # Write the "clean" target: clean_cmds = self._get_clean_commands( mk_fmt, expr_fmt, (build_graphs[t] for t in module.targets.itervalues()), submakefiles.itervalues()) f.write(mk_fmt.target(name="clean", deps=[], commands=clean_cmds)) self.on_phony_targets(f, phony_targets) self.on_footer(f, module) f.commit()