Beispiel #1
0
    def gen_for_module(self, module):
        # attach VS2010-specific data to the model
        module.solution = self.Solution(self, module)

        for t in module.targets.itervalues():
            with error_context(t):
                prj = self.get_project_object(t)
                if prj is None:
                    # TODO: see the TODO in get_project_object()
                    continue
                if prj.name != t.name:
                    # TODO: This is only for the solution file; we should remap the name instead of
                    #       failure. Note that we don't always control prj.name, it may come from external
                    #       project file.
                    raise Error("project name (\"%s\") differs from target name (\"%s\"), they must be the same" %
                                (prj.name, t.name))
                if prj.version and prj.version not in self.proj_versions:
                    if prj.version > self.proj_versions[-1]:
                        raise Error("project %s is for Visual Studio %.1f and will not work with %.1f" %
                                    (prj.projectfile, prj.version, self.version))
                    else:
                        warning("project %s is for Visual Studio %.1f, not %.1f, will be converted when built",
                                prj.projectfile, prj.version, self.version)

                if self.is_natively_supported(t):
                    self.gen_for_target(t, prj)

                module.solution.add_project(prj)
Beispiel #2
0
    def get_project_object(self, target):
        if self.is_natively_supported(target):
            proj = self.Project(target.name,
                                target["%s.guid" % self.name].as_py(),
                                target["%s.projectfile" % self.name],
                                target["deps"].as_py(),
                                [x.config for x in target.configurations],
                                self.get_platforms(target),
                                target.source_pos)
        else:
            # Not natively supported; try if the TargetType has an implementation
            try:
                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

        # See which configurations this target is explicitly disabled in.
        # Notice that we must check _all_ configurations visible in the solution,
        # not just the ones used by this target.
        all_global_configs = (ConfigurationProxy(target, x)
                              for x in target.project.configurations.itervalues())
        proj.disabled_configurations = [x.config for x in all_global_configs
                                        if not x.should_build()]
        return proj
Beispiel #3
0
def detect_unused_vars(model):
    """
    Warns about unused variables -- they may indicate typos.
    """
    # First of all, iterate over all variables and mark their usage of other
    # variables. Notice that it's possible that some code explicitly marked
    # variables as used with mark_variables_as_used() before this step.
    for var in model.all_variables():
        usage_tracker.visit(var.value)

    # Not emit warnings for unused variables.
    import re

    regex_vs_option = re.compile(r"vs[0-9]+\.option\.")

    for var in model.all_variables():
        if (
            not var.is_property
            and not usage_tracker.is_used(var)
            and
            # FIXME: Handle these cases properly. Have a properties group
            #        declaration similar to Property, with type checking and
            #        automated docs and all. Then test for it here as other
            #        properties are tested for.
            not regex_vs_option.match(var.name)
            and
            # FIXME: Handle this case properly.
            var.name != "configurations"
        ):
            warning('variable "%s" is never used', var.name, pos=var.value.pos)
Beispiel #4
0
def detect_missing_generated_outputs(model):
    """
    Warns about generated source files not included in sources/headers.
    """
    for t in model.all_targets():
        for srcfile in t.all_source_files():
            with error_context(srcfile):
                if not srcfile["compile-commands"]:
                    continue
                sources = set(ch.name for ch in t.child_parts())
                outputs = set(i for c,i in bkl.expr.enum_possible_values(srcfile["outputs"]))
                for item in outputs:
                    partname = bkl.expr.get_model_name_from_path(item)
                    if partname not in sources:
                        warning("file %s generated from %s is not among sources or headers of target \"%s\"",
                                item, srcfile.filename, t.name, pos=item.pos)
Beispiel #5
0
    def generate(self):
        """
        Generates output files.
        """
        # collect all requested toolsets:
        toolsets = set()
        for module in self.model.modules:
            module_toolsets = module.get_variable("toolsets")
            if module_toolsets:
                toolsets.update(module_toolsets.value.as_py())

        if self.toolsets_to_use:
            for t in self.toolsets_to_use:
                if t not in toolsets:
                    try:
                        bkl.api.Toolset.get(t)
                    except KeyError:
                        raise Error(
                            "unknown toolset \"%s\" given on command line" % t)
                    warning(
                        "toolset \"%s\" is not supported by the project, there may be issues",
                        t)
                    # Add the forced toolset to all submodules:
                    for module in self.model.modules:
                        module_toolsets = module.get_variable("toolsets")
                        if module_toolsets:
                            module_toolsets.value.items.append(
                                bkl.expr.LiteralExpr(t))
            toolsets = self.toolsets_to_use

        toolsets = list(toolsets)
        logger.debug("toolsets to generate for: %s", toolsets)

        if not toolsets:
            raise Error("nothing to generate, \"toolsets\" property is empty")

        # call any custom steps first:
        self._call_custom_steps(self.model, "generate")

        # and generate the outputs (notice that we can avoid making a
        # (expensive!) deepcopy of the model for one of the toolsets and can
        # reuse the current model):
        for toolset in toolsets[:-1]:
            self.generate_for_toolset(toolset)
        self.generate_for_toolset(toolsets[-1], skip_making_copy=True)
Beispiel #6
0
 def unescape(self, token, text):
     """Removes \\ escapes from the text."""
     out = ""
     start = 0
     while True:
         pos = text.find('\\', start)
         if pos == -1:
             out += text[start:]
             break
         else:
             out += text[start:pos]
             c = text[pos+1]
             out += c
             start = pos+2
             if c != '"' and c != '\\' and c != '$':
                 source_pos = self._get_position(token)
                 source_pos.column += pos+1
                 warning("unnecessary escape sequence '\\%s' (did you mean '\\\\%s'?)" % (c, c),
                         pos=source_pos)
     return out
Beispiel #7
0
    def generate(self):
        """
        Generates output files.
        """
        # collect all requested toolsets:
        toolsets = set()
        for module in self.model.modules:
            module_toolsets = module.get_variable("toolsets")
            if module_toolsets:
                toolsets.update(module_toolsets.value.as_py())

        if self.toolsets_to_use:
            for t in self.toolsets_to_use:
                if t not in toolsets:
                    try:
                        bkl.api.Toolset.get(t)
                    except KeyError:
                        raise Error("unknown toolset \"%s\" given on command line" % t)
                    warning("toolset \"%s\" is not supported by the project, there may be issues", t)
                    # Add the forced toolset to all submodules:
                    for module in self.model.modules:
                        module_toolsets = module.get_variable("toolsets")
                        if module_toolsets:
                            module_toolsets.value.items.append(bkl.expr.LiteralExpr(t))
            toolsets = self.toolsets_to_use

        toolsets = list(toolsets)
        logger.debug("toolsets to generate for: %s", toolsets)

        if not toolsets:
            raise Error("nothing to generate, \"toolsets\" property is empty")

        # call any custom steps first:
        self._call_custom_steps(self.model, "generate")

        # and generate the outputs (notice that we can avoid making a
        # (expensive!) deepcopy of the model for one of the toolsets and can
        # reuse the current model):
        for toolset in toolsets[:-1]:
            self.generate_for_toolset(toolset)
        self.generate_for_toolset(toolsets[-1], skip_making_copy=True)
Beispiel #8
0
def detect_missing_generated_outputs(model):
    """
    Warns about generated source files not included in sources/headers.
    """
    for t in model.all_targets():
        for srcfile in t.all_source_files():
            with error_context(srcfile):
                if not srcfile["compile-commands"]:
                    continue
                sources = set(ch.name for ch in t.child_parts())
                outputs = set(i for c, i in bkl.expr.enum_possible_values(
                    srcfile["outputs"]))
                for item in outputs:
                    partname = bkl.expr.get_model_name_from_path(item)
                    if partname not in sources:
                        warning(
                            "file %s generated from %s is not among sources or headers of target \"%s\"",
                            item,
                            srcfile.filename,
                            t.name,
                            pos=item.pos)
Beispiel #9
0
def _get_matching_project_config(cfg, prj):
    """
    Returns best match project configuration for given solution configuration.
    """
    with error_context(prj):
        if cfg in prj.configurations:
            return cfg

        # else: try to find a configuration closest to the given one, i.e.
        # the one from which it inherits via the minimal number of
        # intermediate configurations:
        compatibles = []
        for pc in prj.configurations:
            degree = cfg.derived_from(pc)
            if degree:
                compatibles.append((degree, pc))

        if not compatibles:
            # if we don't have any project configurations from which this
            # one inherits, check if we have any which inherit from this
            # one themselves as they should be a reasonably good fallback:
            for pc in prj.configurations:
                degree = pc.derived_from(cfg)
                if degree:
                    compatibles.append((degree, pc))

        if compatibles:
            if len(compatibles) > 1:
                compatibles.sort()
                # It can happen that we have 2 project configurations
                # inheriting from the solution configuration with the same
                # degree. In this case there we can't really make the
                # right choice automatically, so we must warn the user.
                degree = compatibles[0][0]
                if compatibles[1][0] == degree:
                    good_ones = [x[1].name for x in compatibles if x[0] == degree]
                    warning("project %s: no unambiguous choice of project configuration to use for the solution configuration \"%s\", equally good candidates are: \"%s\"",
                            prj.projectfile,
                            cfg.name,
                            '", "'.join(good_ones))

            degree, ret = compatibles[0]
            logger.debug("solution config \"%s\" -> project %s config \"%s\" (dg %d)",
                         cfg.name, prj.projectfile, ret.name, degree)
            return ret

        # if all failed, just pick the first config, but at least try to match
        # debug/release setting:
        compatibles = [x for x in prj.configurations if x.is_debug == cfg.is_debug]
        if compatibles:
            ret = compatibles[0]
            warning("project %s: using unrelated project configuration \"%s\" for solution configuration \"%s\"",
                    prj.projectfile, ret.name, cfg.name)
            return ret
        else:
            ret = prj.configurations[0]
            warning("project %s: using incompatible project configuration \"%s\" for solution configuration \"%s\"",
                    prj.projectfile, ret.name, cfg.name)
            return ret
Beispiel #10
0
 def unescape(self, token, text):
     """Removes \\ escapes from the text."""
     out = ""
     start = 0
     while True:
         pos = text.find('\\', start)
         if pos == -1:
             out += text[start:]
             break
         else:
             out += text[start:pos]
             c = text[pos + 1]
             out += c
             start = pos + 2
             if c != '"' and c != '\\' and c != '$':
                 source_pos = self._get_position(token)
                 source_pos.column += pos + 1
                 warning(
                     "unnecessary escape sequence '\\%s' (did you mean '\\\\%s'?)"
                     % (c, c),
                     pos=source_pos)
     return out
Beispiel #11
0
def detect_unused_vars(model):
    """
    Warns about unused variables -- they may indicate typos.
    """
    # First of all, iterate over all variables and mark their usage of other
    # variables. Notice that it's possible that some code explicitly marked
    # variables as used with mark_variables_in_expr_as_used() before this step.
    for var in model.all_variables():
        usage_tracker.visit(var.value)

    # Not emit warnings for unused variables.
    import re
    regex_vs_option = re.compile(r'vs[0-9]+\.option\.')

    for var in model.all_variables():
        if (not var.is_property and not usage_tracker.is_used(var) and
                # FIXME: Handle these cases properly. Have a properties group
                #        declaration similar to Property, with type checking and
                #        automated docs and all. Then test for it here as other
                #        properties are tested for.
                not regex_vs_option.match(var.name) and
                # FIXME: Handle this case properly.
                var.name != "configurations"):
            warning('variable "%s" is never used', var.name, pos=var.value.pos)
Beispiel #12
0
def _get_matching_project_config(cfg, prj):
    """
    Returns best match project configuration for given solution configuration.
    """
    with error_context(prj):
        # If the project doesn't have any configurations, it means that we
        # failed to parse it properly, presumably because it defines its
        # configurations (and platforms, see _get_matching_project_platform()
        # too) in a separately imported file. Ideal would be to follow the
        # import chain, but this is not trivial, e.g. we would need to parse
        # and evaluate MSBuild functions to find the full path of the file
        # being imported, so for now we just optimistically assume that the
        # project supports all solution configurations because it's the only
        # thing we can do, the only alternative would be to refuse to use it
        # completely.
        if cfg in prj.configurations or not prj.configurations:
            return cfg

        # else: try to find a configuration closest to the given one, i.e.
        # the one from which it inherits via the minimal number of
        # intermediate configurations:
        compatibles = []
        for pc in prj.configurations:
            degree = cfg.derived_from(pc)
            if degree:
                compatibles.append((degree, pc))

        if not compatibles:
            # if we don't have any project configurations from which this
            # one inherits, check if we have any which inherit from this
            # one themselves as they should be a reasonably good fallback:
            for pc in prj.configurations:
                degree = pc.derived_from(cfg)
                if degree:
                    compatibles.append((degree, pc))

        if compatibles:
            if len(compatibles) > 1:
                compatibles.sort()
                # It can happen that we have 2 project configurations
                # inheriting from the solution configuration with the same
                # degree. In this case there we can't really make the
                # right choice automatically, so we must warn the user.
                degree = compatibles[0][0]
                if compatibles[1][0] == degree:
                    good_ones = [x[1].name for x in compatibles if x[0] == degree]
                    warning("project %s: no unambiguous choice of project configuration to use for the solution configuration \"%s\", equally good candidates are: \"%s\"",
                            prj.projectfile,
                            cfg.name,
                            '", "'.join(good_ones))

            degree, ret = compatibles[0]
            logger.debug("solution config \"%s\" -> project %s config \"%s\" (dg %d)",
                         cfg.name, prj.projectfile, ret.name, degree)
            return ret

        # if all failed, just pick the first config, but at least try to match
        # debug/release setting:
        compatibles = [x for x in prj.configurations if x.is_debug == cfg.is_debug]
        if compatibles:
            ret = compatibles[0]
            warning("project %s: using unrelated project configuration \"%s\" for solution configuration \"%s\"",
                    prj.projectfile, ret.name, cfg.name)
            return ret
        else:
            ret = prj.configurations[0]
            warning("project %s: using incompatible project configuration \"%s\" for solution configuration \"%s\"",
                    prj.projectfile, ret.name, cfg.name)
            return ret