示例#1
0
    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()
示例#2
0
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()
示例#3
0
    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()