示例#1
0
    def test_msvc_flag_with_value(self):
        parser = ArgumentParserEx(prefix_chars="/-")
        parser.set(ignore_case=True)
        parser.add_argument("/flag", action="msvc_flag_with_value")
        parser.add_argument("/ENABLE",
                            action="msvc_flag_with_value",
                            append=True,
                            dest="list")

        ns, remaining = parser.parse_known_args(["/FLAG"])
        self.assertListEqual(["/FLAG"], remaining)
        self.assertIsNone(vars(ns).get("flag"))

        ns, remaining = parser.parse_known_args(["/flag"])
        self.assertListEqual(["/flag"], remaining)
        self.assertIsNone(vars(ns).get("flag"))

        ns, remaining = parser.parse_known_args(["/flag:"])
        self.assertListEqual(["/flag:"], remaining)
        self.assertIsNone(vars(ns).get("flag"))

        ns = parser.parse_args(["/flag:value"])
        self.assertEqual("value", ns.flag)

        ns = parser.parse_args(["-FLAG:1,2,3"])
        self.assertEqual("1,2,3", ns.flag)

        ns = parser.parse_args(["/ENABLE:1", "-FlAg:2", "-enable:3"])
        self.assertEqual("2", ns.flag)
        self.assertEqual(["1", "3"], ns.list)
示例#2
0
    def test_prefix_chars(self):
        parser = ArgumentParserEx(prefix_chars="/-")
        parser.add_argument("-attr1", action="store")

        ns, remaining = parser.parse_known_args(["/attr1", "val"])
        self.assertListEqual([], remaining)
        self.assertEqual("val", ns.attr1)

        ns, remaining = parser.parse_known_args(["-attr1", "val"])
        self.assertListEqual([], remaining)
        self.assertEqual("val", ns.attr1)

        parser.set(prefix_chars="+")
        parser.add_argument("+attr2", action="store")

        ns, remaining = parser.parse_known_args(
            ["-attr1", "val1", "+attr2", "val2"])
        self.assertListEqual([], remaining)
        self.assertEqual("val1", ns.attr1)
        self.assertEqual("val2", ns.attr2)

        ns, remaining = parser.parse_known_args(
            ["/attr1", "val1", "+attr2", "val2"])
        self.assertListEqual([], remaining)
        self.assertEqual("val1", ns.attr1)
        self.assertEqual("val2", ns.attr2)

        ns, remaining = parser.parse_known_args(
            ["+attr1", "val1", "-attr2", "val2"])
        self.assertListEqual(["+attr1", "val1", "-attr2", "val2"], remaining)
        self.assertIsNone(vars(ns).get("attr1"))
        self.assertIsNone(vars(ns).get("attr2"))
示例#3
0
    def test_unknown_args_dest(self):
        parser = ArgumentParserEx()
        parser.add_argument("--flag", action="store_true", raw_dest="args")
        parser.add_argument("--key", raw_dest="args")
        parser.add_argument("infile", raw_dest="args")

        ns, unknown = parser.parse_known_args(["--flag"], unknown_dest="args")
        self.assertListEqual(["--flag"], ns.args)
        self.assertListEqual([], unknown)

        ns, unknown = parser.parse_known_args(
            ["--flag", "--unknownflag", "--key=value"], unknown_dest="args")
        self.assertListEqual(["--flag", "--unknownflag", "--key=value"],
                             ns.args)
        self.assertListEqual(["--unknownflag"], unknown)

        ns, unknown = parser.parse_known_args(
            ["--flag", "--unknownflag", "a.txt"], unknown_dest="args")
        self.assertListEqual(["--flag", "--unknownflag", "a.txt"], ns.args)
        self.assertListEqual(["--unknownflag"], unknown)

        ns, unknown = parser.parse_known_args(
            ["--flag", "a.txt", "--unknownflag"], unknown_dest="args")
        self.assertListEqual(["--flag", "a.txt", "--unknownflag"], ns.args)
        self.assertListEqual(["--unknownflag"], unknown)

        ns, unknown = parser.parse_known_args(["--unknown"],
                                              unknown_dest=["args", "extra"])
        self.assertListEqual(["--unknown"], ns.args)
        self.assertListEqual(["--unknown"], ns.extra)
        self.assertListEqual(["--unknown"], unknown)
示例#4
0
    def test_raw_dest(self):
        parser = ArgumentParserEx()
        parser.add_argument("-arg1", action="store_true", raw_dest="args")
        parser.add_argument("--arg2", "-f", dest=None, raw_dest="args")
        parser.add_argument("file", raw_dest="args")
        parser.add_argument("files", nargs="*", dest=None, raw_dest="args")
        parser.add_argument("-D",
                            dest=None,
                            raw_dest="args",
                            raw_format="".join)

        ns = parser.parse_args(["--arg2", "1", "2"])
        self.assertListEqual([["--arg2", "1"], "2"], ns.args)

        ns = parser.parse_args(["--arg2=1", "1", "-arg1", "2", "3"])
        self.assertListEqual(["--arg2=1", "1", "-arg1", "2", "3"], ns.args)

        ns, remaining = parser.parse_known_args(
            ["1", "-fvalue", "2", "-unknown"])
        self.assertListEqual(["-unknown"], remaining)
        self.assertListEqual(["1", "-fvalue", "2"], ns.args)

        ns, remaining = parser.parse_known_args(
            ["1", "-unknown", "-fvalue", "2"])
        self.assertListEqual(["-unknown"], remaining)
        self.assertListEqual(["1", "-fvalue", "2"], ns.args)

        ns = parser.parse_args(["1", "-D", "2", "3", "-D4"])
        self.assertListEqual(["1", "-D2", "3", "-D4"], ns.args)
示例#5
0
    def test_action_store_false(self):
        parser = ArgumentParserEx()
        parser.add_argument("-a", action="store_false")

        ns, remaining = parser.parse_known_args([])
        self.assertListEqual([], remaining)
        self.assertTrue(ns.a)

        ns, remaining = parser.parse_known_args(["-a"])
        self.assertListEqual([], remaining)
        self.assertFalse(ns.a)
示例#6
0
    def test_action_store_positional(self):
        parser = ArgumentParserEx()
        parser.add_argument("attr", action="store")

        ns, remaining = parser.parse_known_args(["-val"])
        self.assertListEqual(["-val"], remaining)
        self.assertIsNone(vars(ns).get("attr"))

        ns, remaining = parser.parse_known_args(["val"])
        self.assertListEqual([], remaining)
        self.assertEqual("val", ns.attr)
示例#7
0
    def test_action_append_const(self):
        parser = ArgumentParserEx()
        parser.add_argument("-a", action="append_const", const="v")

        ns, remaining = parser.parse_known_args(["-a", "1", "-a2", "-a"])
        self.assertListEqual(["1", "-a2"], remaining)
        self.assertEqual(["v", "v"], ns.a)
示例#8
0
    def test_mutually_exclusive_group(self):
        self.skipTest("not implemented yet")
        parser = ArgumentParserEx()
        group = parser.add_mutually_exclusive_group()
        group.add_argument("-a", action="store_const", const="a", dest="mode")
        group.add_argument("-b", action="store_const", const="b", dest="mode")

        ns, remaining = parser.parse_known_args(["-ab"])
        self.assertListEqual(["-ab"], remaining)
        self.assertIsNone(vars(ns).get("mode"))

        ns, remaining = parser.parse_known_args(["-a", "-b"])
        self.assertListEqual(["-b"], remaining)
        self.assertEqual("a", ns.mode)

        ns = parser.parse_args(["-b"])
        self.assertEqual("b", ns.mode)
示例#9
0
    def test_action_default(self):
        parser = ArgumentParserEx()
        parser.add_argument("attr1")
        parser.add_argument("--attr2")

        ns, remaining = parser.parse_known_args(["--attr2", "val1", "val2"])
        self.assertListEqual([], remaining)
        self.assertEqual("val2", ns.attr1)
        self.assertEqual("val1", ns.attr2)
示例#10
0
    def test_action_store_const(self):
        parser = ArgumentParserEx()
        parser.add_argument("-a", action="store_const", const="a")

        ns, remaining = parser.parse_known_args([])
        self.assertListEqual([], remaining)
        self.assertIsNone(vars(ns).get("a"))

        ns, remaining = parser.parse_known_args(["-a"])
        self.assertListEqual([], remaining)
        self.assertEqual("a", ns.a)

        parser = ArgumentParserEx()
        parser.add_argument("-a", action="store_const", const="a", default="b")

        ns, remaining = parser.parse_known_args([])
        self.assertListEqual([], remaining)
        self.assertEqual("b", ns.a)
示例#11
0
    def test_action_append(self):
        parser = ArgumentParserEx()
        parser.add_argument("-a", action="append")

        ns, remaining = parser.parse_known_args(["-a1", "-a2"])
        self.assertListEqual([], remaining)
        self.assertEqual(["1", "2"], ns.a)

        ns, remaining = parser.parse_known_args(["-a", "2", "-a", "1"])
        self.assertListEqual([], remaining)
        self.assertEqual(["2", "1"], ns.a)

        parser.add_argument("b", action="append")

        ns, remaining = parser.parse_known_args(["-a", "1", "2", "-a", "3"])
        self.assertListEqual([], remaining)
        self.assertEqual(["1", "3"], ns.a)
        self.assertEqual(["2"], ns.b)
示例#12
0
    def test_dest_is_none(self):
        parser = ArgumentParserEx()
        parser.add_argument("-opt", dest=None, required=True)
        parser.add_argument("pos", dest=None)

        ns = parser.parse_args(["-opt", "1", "2"])
        self.assertIsNone(vars(ns).get("opt"))
        self.assertIsNone(vars(ns).get("pos"))

        ns, remaining = parser.parse_known_args(["-opt"])
        self.assertListEqual(["-opt"], remaining)

        ns, remaining = parser.parse_known_args(["1"])
        self.assertListEqual([], remaining)
        self.assertIsNone(vars(ns).get("opt"))
        self.assertIsNone(vars(ns).get("pos"))

        self.assertRaisesRegexp(ValueError, "Required attribute not found",
                                parser.parse_args, ["1"])
示例#13
0
 def test_shortening(self):
     parser = ArgumentParserEx()
     parser.add_argument("--abc")
     parser.add_argument("--a")
     # standard ArgumentParser will parse --ab as --abc
     ns, remaining = parser.parse_known_args(
         ["--abc", "1", "--ab", "2", "--a", "3"])
     self.assertListEqual(["--ab", "2"], remaining)
     self.assertEqual("1", ns.abc)
     self.assertEqual("3", ns.a)
示例#14
0
    def test_choices(self):
        parser = ArgumentParserEx()
        parser.add_argument("--a1", choices=["abc", "ab", "a"])
        parser.add_argument("a2", choices=["def", "ef"])

        ns, remaining = parser.parse_known_args(["--a1=abc", "de"])
        self.assertListEqual(["de"], remaining)
        self.assertEqual("abc", ns.a1)
        self.assertIsNone(vars(ns).get("a2"))

        ns, remaining = parser.parse_known_args(["--a1", "abc", "de"])
        self.assertListEqual(["de"], remaining)
        self.assertEqual("abc", ns.a1)
        self.assertIsNone(vars(ns).get("a2"))

        ns, remaining = parser.parse_known_args(["--a1", "b", "def"])
        self.assertListEqual(["--a1", "b"], remaining)
        self.assertIsNone(vars(ns).get("a1"))
        self.assertEqual("def", ns.a2)
示例#15
0
    def test_values_starting_with_prefix(self):
        # standard ArgumentParser does not accept values starting with prefix_chars

        parser = ArgumentParserEx()
        parser.add_argument("-Xclang")
        ns = parser.parse_args(["-Xclang", "-no-color-output"])
        self.assertEqual("-no-color-output", ns.Xclang)

        parser = ArgumentParserEx()
        parser.add_argument("-f", nargs="2")
        ns = parser.parse_args(["-f", "-1", "-2"])
        self.assertListEqual(["-1", "-2"], ns.f)

        parser = ArgumentParserEx()
        parser.add_argument("-f", nargs="2", action="append")
        parser.add_argument("--enable", action="store_true")
        ns = parser.parse_args(["-f", "-1", "-2"])
        self.assertListEqual([["-1", "-2"]], ns.f)
        parser.add_argument("-f", nargs="2", action="append")
        ns = parser.parse_args(["-f", "-1", "-2", "-f", "-3", "-4"])
        self.assertListEqual([["-1", "-2"], ["-3", "-4"]], ns.f)
        ns, remaining = parser.parse_known_args(["-f", "-1", "-2", "-f", "-3"])
        self.assertListEqual(["-f", "-3"], remaining)
        self.assertListEqual([["-1", "-2"]], ns.f)
        ns, remaining = parser.parse_known_args(
            ["-f", "-1", "-2", "-f", "-3", "--enable", "-4"])
        self.assertListEqual(["-4"], remaining)
        self.assertListEqual([["-1", "-2"], ["-3", "--enable"]], ns.f)
        self.assertFalse(ns.enable)
        self.assertRaisesRegexp(
            ValueError,
            "Unparsed tokens",
            parser.parse_args,
            ["-f", "-1", "-2", "-f", "-3"],
        )

        parser = ArgumentParserEx()
        parser.add_argument("f", nargs="*")
        ns, remaining = parser.parse_known_args(["-1"])
        self.assertListEqual(["-1"], remaining)
        ns, remaining = parser.parse_known_args(["1", "2", "-3"])
        self.assertListEqual(["-3"], remaining)
        self.assertListEqual(["1", "2"], ns.f)
        ns, remaining = parser.parse_known_args(["1", "-2", "3"])
        self.assertListEqual(["-2"], remaining)
        self.assertListEqual(["1", "3"], ns.f)

        parser = ArgumentParserEx()
        parser.add_argument("f", nargs="+")
        ns, remaining = parser.parse_known_args(["-1", "-2"])
        self.assertListEqual(["-1", "-2"], remaining)

        parser = ArgumentParserEx()
        parser.add_argument("-f", nargs="+")
        ns, remaining = parser.parse_known_args(["-f", "-1", "-2"])
        self.assertListEqual(["-2"], remaining)
        self.assertListEqual(["-1"], ns.f)
示例#16
0
    def test_action_store_override(self):
        parser = ArgumentParserEx()
        parser.add_argument("attr1", action="store")
        parser.add_argument("--attr2", action="store")

        ns, remaining = parser.parse_known_args(["val1", "--attr2", "val2"])
        self.assertListEqual([], remaining)
        self.assertEqual("val1", ns.attr1)
        self.assertEqual("val2", ns.attr2)

        ns, remaining = parser.parse_known_args(["--attr2", "val2", "val1"])
        self.assertListEqual([], remaining)
        self.assertEqual("val1", ns.attr1)
        self.assertEqual("val2", ns.attr2)

        ns, remaining = parser.parse_known_args(
            ["--attr2", "val1", "val2", "--attr2", "val3"])
        self.assertListEqual([], remaining)
        self.assertEqual("val2", ns.attr1)
        self.assertEqual("val3", ns.attr2)
示例#17
0
    def test_type_conversion(self):
        parser = ArgumentParserEx()
        parser.add_argument("--a1", type=int)
        # disable prefix chars to allow negative numbers as positional arguments
        parser.set(prefix_chars="")
        parser.add_argument("a2", type=float)

        ns, remaining = parser.parse_known_args(["--a1", "h", "0.1"])
        self.assertListEqual(["--a1", "h"], remaining)
        self.assertIsNone(vars(ns).get("a1"))
        self.assertEqual(0.1, ns.a2)

        ns, remaining = parser.parse_known_args(["-1", "--a1=123"])
        self.assertListEqual([], remaining)
        self.assertEqual(123, ns.a1)
        self.assertEqual(-1, ns.a2)

        ns, remaining = parser.parse_known_args(["--a1", "-5", "-1"])
        self.assertListEqual([], remaining)
        self.assertEqual(-5, ns.a1)
        self.assertEqual(-1, ns.a2)
示例#18
0
    def test_positionals_do_not_consume_optionals(self):
        parser = ArgumentParserEx()
        parser.add_argument("positional")
        parser.add_argument("-optional", action="store_true")
        ns, remaining = parser.parse_known_args(["-optional"])
        self.assertListEqual([], remaining)
        self.assertTrue(ns.optional)
        self.assertIsNone(ns.positional)

        parser = ArgumentParserEx()
        parser.add_argument("positional", nargs="+")
        parser.add_argument("-optional", action="store_true")
        ns, remaining = parser.parse_known_args(["-optional"])
        self.assertListEqual([], remaining)
        self.assertTrue(ns.optional)
        self.assertIsNone(ns.positional)

        parser = ArgumentParserEx()
        parser.add_argument("positional", nargs="*")
        parser.add_argument("-optional", action="store_true")
        ns, remaining = parser.parse_known_args(["-optional"])
        self.assertListEqual([], remaining)
        self.assertTrue(ns.optional)
        self.assertEqual([], ns.positional)
示例#19
0
    def test_conditional_arguments(self):
        self.skipTest("not implemented yet")
        parser = ArgumentParserEx()
        arg = parser.add_argument("-a")
        parser.add_argument("-b", after=[arg])

        ns = parser.parse_args(["-a", "1", "-b", "2"])
        self.assertEqual("1", ns.a)
        self.assertEqual("2", ns.b)

        ns = parser.parse_args(["-a", "1"])
        self.assertEqual("1", ns.a)

        ns, remaining = parser.parse_known_args(["-b", "1"])
        self.assertListEqual(["-b", "1"], remaining)
        self.assertIsNone(vars(ns).get("b"))
示例#20
0
    def test_msvc_flag(self):
        parser = ArgumentParserEx(prefix_chars="/")
        parser.add_argument("/GR", action="msvc_flag", dest="rtti")
        parser.add_argument(
            "/INCREMENTAL",
            action="msvc_flag",
            msvc_false_suffix=":NO",
            dest="incremental",
        )
        parser.add_argument(
            "/flag",
            action="msvc_flag",
            msvc_true_suffix=":on",
            msvc_false_suffix=":off",
            ignore_case=True,
        )

        ns = parser.parse_args(["/GR-"])
        self.assertFalse(ns.rtti)
        ns = parser.parse_args(["/GR"])
        self.assertTrue(ns.rtti)
        ns = parser.parse_args(["/INCREMENTAL"])
        self.assertTrue(ns.incremental)
        ns = parser.parse_args(["/INCREMENTAL:NO"])
        self.assertFalse(ns.incremental)
        ns = parser.parse_args(["/FLAG:ON"])
        self.assertTrue(ns.flag)
        ns = parser.parse_args(["/flag:off"])
        self.assertFalse(ns.flag)

        ns = parser.parse_args([])
        self.assertIsNone(vars(ns).get("flag"))
        self.assertIsNone(vars(ns).get("rtti"))
        self.assertIsNone(vars(ns).get("incremental"))

        ns, remaining = parser.parse_known_args(["/flag:unknown"])
        self.assertListEqual(["/flag:unknown"], remaining)
        self.assertIsNone(vars(ns).get("flag"))

        parser.set_defaults(flag=False)
        ns = parser.parse_args([])
        self.assertFalse(ns.flag)
示例#21
0
class MsvcLink(LinkerParser):
    filename_re = os_ext.Windows.get_program_path_re("link")
    lld_link_re = os_ext.Windows.get_program_path_re("lld-link")

    priority = 7

    def __init__(self, context, project_version=None, ignore_link_flags=None):
        LinkerParser.__init__(self, context, ignore_link_flags=ignore_link_flags)

        self.project_version = project_version

        # Visual Studio link.exe arguments
        # https://docs.microsoft.com/en-us/cpp/build/reference/linker-options?view=vs-2017
        self.parser = ArgumentParserEx(prefix_chars="/-")
        self.parser.set_defaults(
            compile_flags=[], link_flags=[], include_dirs=[], infiles=[], lib_dirs=[]
        )
        # TODO: publish all meaningful flags
        self.parser.set(
            ignore_case=True,
            action="msvc_flag_with_value",
            raw_dest="link_flags",
            dest=None,
        )
        # TODO: handle /wholearchive
        self.parser.add_argument("/base", action="msvc_flag_with_value")
        self.parser.add_argument("/debug")
        self.parser.add_argument("/debug", action="store_true")
        self.parser.add_argument("/fixed", action="msvc_flag", msvc_false_suffix=":no")
        self.parser.add_argument("/delayload", action="msvc_flag_with_value")
        self.parser.add_argument("/def")
        self.parser.add_argument(
            "/dll", action="store_true", dest="is_dll", raw_dest=None
        )
        self.parser.add_argument(
            "/dynamicbase", action="msvc_flag", msvc_false_suffix=":no"
        )
        self.parser.add_argument("/errorreport")
        self.parser.add_argument("/fastfail", action="store_true")
        self.parser.add_argument("/guard", action="msvc_flag_with_value")
        # Ignore Specific Warnings
        self.parser.add_argument("/ignore", action="msvc_flag_with_value")
        self.parser.add_argument("/implib", dest="implib", raw_dest=None)
        self.parser.add_argument(
            "/incremental", action="msvc_flag", msvc_false_suffix=":no"
        )
        self.parser.add_argument(
            "/largeaddressaware", action="msvc_flag", msvc_false_suffix=":no"
        )
        self.parser.add_argument(
            "/libpath", append=True, dest="lib_dirs", raw_dest=None
        )
        self.parser.add_argument("/ltcg")
        self.parser.add_argument("/ltcg", action="store_true")
        self.parser.add_argument("/machine")
        self.parser.add_argument("/manifest")
        self.parser.add_argument("/manifest", action="store_true")
        self.parser.add_argument(
            "/manifestinput", raw_dest=None, dest="manifest_files", append=True
        )
        self.parser.add_argument(
            "/manifestfile", raw_dest=None, dest="manifest_files", append=True
        )
        self.parser.add_argument("/manifestuac")
        self.parser.add_argument("/manifestuac", action="store_true")
        self.parser.add_argument("/map")
        self.parser.add_argument("/map", action="store_true")
        self.parser.add_argument("/mapinfo")
        self.parser.add_argument("/noentry", action="store_true")
        self.parser.add_argument("/nologo", action="store_true")
        self.parser.add_argument("/natvis", action="msvc_flag_with_value")
        self.parser.add_argument(
            "/nxcompat", action="msvc_flag", msvc_false_suffix=":no"
        )
        self.parser.add_argument("/opt", append=True)
        self.parser.add_argument("/out", dest="output", raw_dest=None)
        self.parser.add_argument("/pdb")
        self.parser.add_argument("/pdbaltpath")
        self.parser.add_argument("/profile", action="store_true")
        self.parser.add_argument("/safeseh", action="store_true")
        self.parser.add_argument("/stack")
        self.parser.add_argument("/subsystem")
        self.parser.add_argument("/timestamp")
        self.parser.add_argument("/tlbid")
        self.parser.add_argument("/version")
        self.parser.add_argument("/wholearchive", append=True)
        self.parser.add_argument("/wholearchive", action="store_true")
        self.parser.add_argument("/WX", action="msvc_flag", msvc_false_suffix=":no")
        self.parser.set(action=None)
        self.parser.add_argument("infiles", nargs="*", raw_dest=None, dest="infiles")

        # lld-link.exe arguments
        # Cannot find documentation online
        self.lld_link_parser = deepcopy(self.parser)
        self.lld_link_parser.add_argument("/llvmlibthin", action="store_true")
        self.lld_link_parser.set(prefix_chars="-")
        self.lld_link_parser.add_argument("--color-diagnostics", action="store_true")

    def _process_link_flags(self, flags, dependencies):
        for idx, flag in enumerate(flags):
            flag_lower = flag.lower()
            if flag_lower[0] == "/":
                flag_lower = "-" + flag_lower[1:]
            if flag_lower.startswith("-def:"):
                flags[idx] = flag[:5] + self.context.get_file_arg(
                    flag[5:], dependencies
                )
        return flags

    def parse(self, target):
        tokens = target.get("tokens") or []
        if not tokens:
            return target

        if not self.filename_re.match(tokens[0]):
            return target
        else:
            if is_lib_shim(tokens):
                return target

        if len(tokens) < 2 or tokens[1] == ":":
            # skip warning message
            return target

        is_lld_link = bool(self.lld_link_re.match(tokens[0]))
        tokens.pop(0)

        if is_lld_link:
            namespace, _ = self.lld_link_parser.parse_known_args(
                tokens, unknown_dest=["compile_flags", "link_flags"]
            )
        else:
            namespace, _ = self.parser.parse_known_args(
                tokens, unknown_dest=["compile_flags", "link_flags"]
            )

        dependencies = []

        lib_dirs = list(
            map(
                lambda p: p[len("/LIBPATH:"):],
                filter_flags(
                    self.ignore_link_flags_rxs,
                    ["/LIBPATH:" + d for d in namespace.lib_dirs],
                ),
            )
        )
        for d in lib_dirs:
            d = self.context.normalize_path(d)
            relocatable_path = self.context.get_dir_arg(d)
            if relocatable_path.startswith("@"):
                # ignore non-relocatable lib dirs
                self.context.get_dir_arg(d, dependencies)

        objects = []
        libs = []
        for infile in namespace.infiles:
            if os_ext.Windows.is_static_lib(infile):
                libs.append(self.context.get_lib_arg(infile, dependencies, lib_dirs))
            else:
                paths = os_ext.Windows.get_obj_file_locations(
                    infile, lib_dirs, self.context.working_dir
                )
                found = False
                for path in paths:
                    if self.context.find_target(
                        self.context.get_file_arg(path)
                    ) or os.path.exists(path):
                        objects.append(self.context.get_file_arg(path, dependencies))
                        found = True
                        break
                if not found:
                    # System object file, treat it as a linker flag
                    # https://docs.microsoft.com/en-us/cpp/c-runtime-library/link-options
                    objects.append(infile)

        namespace.link_flags = self._process_link_flags(
            namespace.link_flags, dependencies
        )

        sources = []
        for manifest in vars(namespace).get("manifest_files") or []:
            sources.append(
                get_source_file_reference(
                    self.context.get_file_arg(
                        self.context.normalize_path(manifest), dependencies
                    )
                )
            )

        import_lib = None
        if vars(namespace).get("implib"):
            import_lib = self.context.get_output(namespace.implib, dependencies)

        output = self.context.normalize_path(namespace.output)
        if os_ext.Windows.is_shared_lib(output):
            # DLL file is created if:
            # 1. /DLL flag was specified, or
            # 2. LIBRARY statement is present in provided .def file
            # TODO: validate these conditions here
            descr = os_ext.Windows.parse_shared_lib(output)
            module_type = ModuleTypes.shared_lib
            if not import_lib:
                import_lib = self.context.get_output(
                    os.path.splitext(namespace.output)[0] + ".lib", dependencies
                )
        else:
            descr = os_ext.Windows.parse_executable(output)
            module_type = ModuleTypes.executable

        module_name = descr["module_name"]
        name = descr["target_name"]
        version = descr["version"] or self.project_version
        output = self.context.get_output(output, dependencies)

        self.process_namespace(namespace)

        return get_module_target(
            module_type,
            name,
            output,
            msvc_import_lib=import_lib,
            dependencies=dependencies,
            module_name=module_name,
            objects=objects,
            libs=libs,
            link_flags=namespace.link_flags,
            sources=sources,
            version=version,
        )
示例#22
0
class MsvcCl(CompilerParser, LinkerParser):
    filename_re = os_ext.Windows.get_program_path_re("cl")
    clang_cl_re = os_ext.Windows.get_program_path_re("clang-cl")

    c_exts = [".c"]
    cpp_exts = [".cc", ".cpp", ".cxx"]
    source_exts = c_exts + cpp_exts

    priority = 7

    @staticmethod
    def add_arguments(arg_parser):
        CompilerParser.add_arguments(arg_parser)
        LinkerParser.add_arguments(arg_parser)

    def __init__(self,
                 context,
                 ignore_compile_flags=None,
                 ignore_link_flags=None):
        CompilerParser.__init__(self,
                                context,
                                ignore_compile_flags=ignore_compile_flags)
        LinkerParser.__init__(self,
                              context,
                              ignore_link_flags=ignore_link_flags)

        # /showIncludes flag doesn't allow filtering toolchain includes,
        # se we have to do that ourselves.
        msvc_include_dirs = os.environ.get("INCLUDE", [])
        if msvc_include_dirs:
            self.msvc_include_dirs = prepare_toolchain_include_dirs(
                msvc_include_dirs.split(";"), context)
        else:
            self.msvc_include_dirs = []
        logger.debug("MSVC include dirs: %r" % self.msvc_include_dirs)
        self.clang_cl_include_dirs = None

        # Visual Studio cl.exe arguments
        # https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically?view=vs-2017
        self.parser = ArgumentParserEx(prefix_chars="-/")
        self.parser.set_defaults(compile_flags=[],
                                 link_flags=[],
                                 include_dirs=[],
                                 infiles=[])
        # TODO: publish all meaningful flags
        # TODO: specify raw_format= for each argument
        self.parser.set(dest=None, raw_dest="compile_flags")
        # Enables code analysis and control options
        self.parser.add_argument("/analyze", prefix=True)
        self.parser.add_argument("/arch", action="msvc_flag_with_value")
        self.parser.add_argument(
            "/bigobj",
            action="store_true",
            raw_format=format_flag_msvc_lowercase,
            ignore_case=True,
        )
        # Emit an object file which can be reproduced over time
        self.parser.add_argument("/Brepro", action="msvc_flag")
        self.parser.add_argument("/c",
                                 action="store_true",
                                 dest="compile_only",
                                 raw_dest=None)
        self.parser.add_argument("/D", raw_format=format_flag_gnu)
        # /d1xxx = undocumented frontend options
        self.parser.add_argument(prefixes=["/d1", "-d1"])
        # /d2xxx = undocumented backend options
        self.parser.add_argument(prefixes=["/d2", "-d2"])
        self.parser.add_argument("/diagnostics", action="msvc_flag_with_value")
        # Exception Handling Model
        self.parser.add_argument(prefixes=["/EH", "-EH"])
        self.parser.add_argument("/errorreport",
                                 action="msvc_flag_with_value",
                                 ignore_case=True)
        self.parser.add_argument("/favor", action="msvc_flag_with_value")
        self.parser.add_argument("/FC", action="store_true")
        # Force include
        # TODO: process path arg declaratively (type= argument?)
        self.parser.add_argument("/FI", prefix=True)
        self.parser.add_argument("/FR", prefix=True)
        # IDE minimal rebuild
        self.parser.add_argument("/FD", action="store_true")
        # Detect 64-Bit Portability Issues
        self.parser.add_argument("/Wp64", action="store_true")
        # Floating point behavior
        self.parser.add_argument("/fp", action="msvc_flag_with_value")
        # Force Synchronous PDB Writes
        self.parser.add_argument("/FS", action="store_true")
        # Program Database File Name
        self.parser.add_argument("/Fd", prefix=True)
        # Output: exe, dll
        self.parser.add_argument("/Fe",
                                 prefix=True,
                                 dest="output",
                                 raw_dest=None)
        # Output: obj
        self.parser.add_argument("/Fo",
                                 prefix=True,
                                 dest="output",
                                 raw_dest=None)
        # Precompiled header (.pch) file name
        # TODO: process path arg
        self.parser.add_argument("/Fp", prefix=True)
        # Eliminate Duplicate Strings
        self.parser.add_argument("/GF", action="store_true")
        # Whole Program Optimization
        self.parser.add_argument("/GL", action="msvc_flag")
        # Full path of source code file in diagnostics
        self.parser.add_argument("/FC", action="msvc_flag")
        # Runtime Type Information
        self.parser.add_argument("/GR",
                                 action="msvc_flag",
                                 raw_format=format_flag_msvc)
        # Calling convention
        self.parser.add_argument("/Gd",
                                 "/Gr",
                                 "/Gv",
                                 "/Gz",
                                 action="store_true")
        # Minimal rebuild
        self.parser.add_argument("/Gm", action="msvc_flag", raw_dest=None)
        # /sdl - Enable additional security check
        self.parser.add_argument("/sdl-", action="msvc_flag")
        # Buffer Security Check
        self.parser.add_argument("/GS", action="msvc_flag")
        # Control Stack Checking Calls
        self.parser.add_argument("/Gs", action="store_true")
        self.parser.add_argument(prefixes=["/Gs", "-Gs"], type=int)
        # Control Flow Guard
        self.parser.add_argument("/guard:cf", action="msvc_flag")
        # Optimize Global Data
        self.parser.add_argument("/Gw", action="msvc_flag")
        # Enable Function-Level Linking
        self.parser.add_argument("/Gy", action="msvc_flag")
        # TODO: process path arg
        self.parser.add_argument("/I",
                                 action="append",
                                 dest="include_dirs",
                                 raw_dest=None)
        # Create dll
        self.parser.add_argument("/LD",
                                 "/LDd",
                                 action="store_true",
                                 raw_dest=None,
                                 dest="is_dll")
        # Passes one or more linker options to the linker
        # The /link option and its linker options must appear after
        # any file names and CL options
        self.parser.add_argument("/link", nargs="+", raw_dest="link_flags")
        # Dynamic runtime
        self.parser.add_argument("/MD", "/MDd", action="store_true")
        self.parser.add_argument("/MP", action="store_true")
        self.parser.add_argument(prefixes=["/MP", "-MP"], type=int)
        # Static runtime
        self.parser.add_argument("/MT", "/MTd", action="store_true")
        self.parser.add_argument("/nologo",
                                 action="store_true",
                                 ignore_case=True)
        # Optimize Code
        self.parser.add_argument(prefixes=["/O", "-O"], choices="12bdgistxy")
        # Inline Function Expansion
        self.parser.add_argument(prefixes=["/Ob", "-Ob"], choices="012")
        # Frame-Pointer Omission
        self.parser.add_argument("/Oy", action="msvc_flag")
        self.parser.add_argument("/showIncludes", action="store_true")
        self.parser.add_argument("/source-charset",
                                 action="msvc_flag_with_value")
        self.parser.add_argument("/std", action="msvc_flag_with_value")
        self.parser.add_argument("/TC", action="store_true")
        self.parser.add_argument("/TP", action="store_true")
        # C file
        # TODO: process path arg
        self.parser.add_argument("/Tc")
        # C++ file
        # TODO: process path arg
        self.parser.add_argument("/Tp")
        self.parser.add_argument("/utf-8", action="store_true")
        self.parser.add_argument("/U", raw_format=format_flag_gnu)
        # Ignore system PATH and INCLUDE
        self.parser.add_argument("/X", action="store_true")
        # Create Precompiled Header File
        self.parser.add_argument("/Yc", action="store_true")
        self.parser.add_argument("/Yc", prefix=True)
        # Use Precompiled Header File
        self.parser.add_argument("/Yu", action="store_true")
        self.parser.add_argument("/Yu", prefix=True)
        # Debug Information Format
        self.parser.add_argument("/Z7", "/Zi", "/Zl", action="store_true")
        # Conformance
        self.parser.add_argument("/Zc", action="msvc_flag_with_value")
        # Syntax Check Only
        self.parser.add_argument("/Zs",
                                 dest="syntax_check_only",
                                 raw_dest=None,
                                 action="store_true")
        # Precompiled Header Memory Allocation Limit
        self.parser.add_argument("/Zm", prefix=True)
        self.parser.add_argument("/w",
                                 "/W0",
                                 "/W1",
                                 "/W2",
                                 "/W3",
                                 "/W4",
                                 "/Wall",
                                 action="store_true")
        self.parser.add_argument("/WX", action="msvc_flag")
        self.parser.add_argument(prefixes=[
            "/Wv",
            "/w1",
            "/w2",
            "/w3",
            "/w4",
            "/wd",
            "/we",
            "/wo",
            "-Wv",
            "-w1",
            "-w2",
            "-w3",
            "-w4",
            "-wd",
            "-we",
            "-wo",
        ])
        # TODO: process path arg
        self.parser.add_argument("infiles",
                                 nargs="*",
                                 dest="infiles",
                                 raw_dest=None)

        # clang-cl.exe arguments
        # https://clang.llvm.org/docs/UsersManual.html#id9
        self.clang_cl_parser = deepcopy(self.parser)
        self.clang_cl_parser.set(prefix_chars="-")
        self.clang_cl_parser.add_argument("-imsvc", prefix=True)
        self.clang_cl_parser.add_argument("-Xclang")
        self.clang_cl_parser.add_argument(prefixes=["-f"])
        # Processor options
        self.clang_cl_parser.add_argument(prefixes=["-m"])
        self.clang_cl_parser.add_argument("-no-canonical-prefixes",
                                          action="store_true")
        self.clang_cl_parser.add_argument(prefixes=["-std="])
        self.clang_cl_parser.add_argument(prefixes=["-W"])

        self.link_parser = get_msvc_link_parser(
            context, ignore_link_flags=ignore_link_flags)

    re_str = r"^Note: including file:\s+(?P<path>[^\s].*)$"
    include_note_re = re.compile(re_str, re.IGNORECASE)

    def _add_implicit_dependencies(
        self,
        compiler,
        dependencies,
        compile_flags,
        include_dirs,
        sources,
        cwd,
        is_clang_cl=False,
    ):
        if not sources:
            return

        _compiler = os.path.join(cwd, compiler)
        if os.path.exists(_compiler):
            compiler = _compiler

        cmd = [compiler, "/Zs", "/showIncludes"]
        cmd += ["-I" + d
                for d in include_dirs] + flatten_list(compile_flags) + sources
        try:
            stdout, stderr = subprocess_ex.check_output(cmd, cwd=cwd)
        except subprocess_ex.CalledProcessError as e:
            logger.error("{}\nstderr:\n{}stdout:\n{}".format(
                e, e.stderr, e.stdout))
            return

        toolchain_include_dirs = self.msvc_include_dirs
        if is_clang_cl and self.clang_cl_include_dirs:
            toolchain_include_dirs = self.clang_cl_include_dirs
        for line in stdout.splitlines():
            m = self.include_note_re.match(line.strip())
            if m:
                try:
                    path = self.context.normalize_path(m.group("path"))
                    is_toolchain_header = False
                    for d in toolchain_include_dirs:
                        if path.startswith(d):
                            is_toolchain_header = True
                            break
                    if not is_toolchain_header:
                        self.context.get_file_arg(path, dependencies)
                except ValueError:
                    # Path is on drive c:, build dir on drive d:
                    pass

    def _get_clang_cl_toolchain_include_dirs(self, compiler):
        try:
            clang_cl_include_dirs_c = get_gcc_toolchain_include_dirs(
                compiler, self.context, "c")
        except subprocess_ex.CalledProcessError as e:
            logger.error("{}\nstderr:\n{}stdout:\n{}".format(
                e, e.stderr, e.stdout))
            return []
        try:
            clang_cl_include_dirs_cpp = get_gcc_toolchain_include_dirs(
                compiler, self.context, "c++")
        except subprocess_ex.CalledProcessError as e:
            logger.error("{}\nstderr:\n{}stdout:\n{}".format(
                e, e.stderr, e.stdout))
            return []
        include_dirs = set(clang_cl_include_dirs_c)
        include_dirs.update(clang_cl_include_dirs_cpp)
        return [self.context.normalize_path(d) for d in include_dirs]

    def parse(self, target):
        tokens = target.get("tokens")
        if not tokens:
            return target

        if not self.filename_re.match(tokens[0]):
            return target

        if len(tokens) < 2 or tokens[1] == ":":
            # skip warning message
            return target

        compiler = tokens.pop(0)
        compiler_nrm = self.context.platform.normalize_path(compiler)
        is_clang_cl = bool(self.clang_cl_re.match(compiler))
        if compiler_nrm in self.context.path_aliases:
            compiler = self.context.path_aliases[compiler_nrm]

        if not is_clang_cl:
            namespace, _ = self.parser.parse_known_args(
                tokens, unknown_dest="compile_flags")
        else:
            namespace, _ = self.clang_cl_parser.parse_known_args(
                tokens, unknown_dest="compile_flags")
            if self.clang_cl_include_dirs is None:
                self.clang_cl_include_dirs = self._get_clang_cl_toolchain_include_dirs(
                    compiler)
                logger.debug("clang-cl include dirs: %r" %
                             self.clang_cl_include_dirs)

        lib_dirs = []
        dependencies = []
        if namespace.link_flags:
            link_flags = namespace.link_flags[0]
            namespace.link_flags = []
            namespace, _ = self.link_parser.parse_known_args(
                link_flags[1:], namespace, unknown_dest=["link_flags"])

            # Copied from msvc_link.py
            # TODO: unify msvc_cl.py, msvc_link.py, msvc_lib.py
            lib_dirs = list(
                map(
                    lambda p: p[len("/LIBPATH:"):],
                    filter_flags(
                        self.ignore_link_flags_rxs,
                        ["/LIBPATH:" + d for d in namespace.lib_dirs],
                    ),
                ))
            for d in lib_dirs:
                d = self.context.normalize_path(d)
                relocatable_path = self.context.get_dir_arg(d)
                if relocatable_path.startswith("@"):
                    # ignore non-relocatable lib dirs
                    self.context.get_dir_arg(d, dependencies)

        if namespace.syntax_check_only:
            return target

        sources = []
        src_info = {}
        objects = []
        libs = []
        for infile in namespace.infiles:
            ext = os.path.splitext(infile)[1].lower()
            language = None
            if ext in self.c_exts:
                language = "C"
            elif ext in self.cpp_exts:
                language = "C++"

            if language:
                src_deps = []
                relocatable_path = self.context.get_file_arg(
                    self.context.normalize_path(infile), src_deps)
                src_info[relocatable_path] = {
                    "unmodified_path": infile,
                    "dependencies": src_deps,
                }
                sources.append(
                    get_source_file_reference(relocatable_path, language))
            else:
                # Copied from msvc_link.py
                # TODO: unify msvc_cl.py, msvc_link.py, msvc_lib.py
                if os_ext.Windows.is_static_lib(infile):
                    libs.append(
                        self.context.get_lib_arg(infile, dependencies,
                                                 lib_dirs))
                else:
                    paths = os_ext.Windows.get_obj_file_locations(
                        infile, lib_dirs, self.context.working_dir)
                    found = False
                    for path in paths:
                        if self.context.find_target(
                                self.context.get_file_arg(
                                    path)) or os.path.exists(path):
                            objects.append(
                                self.context.get_file_arg(path, dependencies))
                            found = True
                            break
                    if not found:
                        # System object file, treat it as a linker flag
                        # https://docs.microsoft.com/en-us/cpp/c-runtime-library/link-options
                        objects.append(infile)

        CompilerParser.process_namespace(self, namespace)

        include_dirs_not_relocatable = namespace.include_dirs

        include_dirs = list(
            map(
                lambda d: self.context.get_dir_arg(
                    self.context.normalize_path(d), dependencies),
                include_dirs_not_relocatable,
            ))
        namespace.include_dirs = list(filter(lambda d: bool(d), include_dirs))

        output = None
        if namespace.output[-1] in ["/", "\\"]:
            # output is a directory
            output_dir = self.context.normalize_path(namespace.output)
        else:
            output = self.context.normalize_path(namespace.output)

        output_dict = {}
        if output:
            output_dict[output] = sources
        else:
            assert namespace.compile_only
            # put .obj files in output_dir
            # separate .obj file for each source
            for s in sources:
                basename = os.path.basename(s["path"])
                objfile = os.path.splitext(basename)[0] + ".obj"
                cur_output = os.path.join(output_dir, objfile)
                output_dict[cur_output] = [s]

        targets = []
        for output, sources in output_dict.items():
            deps_local = deepcopy(dependencies)
            original_sources = []
            for s in sources:
                deps_local.extend(src_info[s["path"]]["dependencies"])
                original_sources.append(src_info[s["path"]]["unmodified_path"])
            self._add_implicit_dependencies(
                compiler,
                deps_local,
                namespace.compile_flags,
                include_dirs_not_relocatable,
                original_sources,
                self.context.working_dir,
                is_clang_cl=is_clang_cl,
            )
            import_lib = None
            descr = {}
            if namespace.compile_only:
                module_type = ModuleTypes.object_lib
            elif namespace.is_dll:
                assert os_ext.Windows.is_shared_lib(output), output
                module_type = ModuleTypes.shared_lib
                descr = os_ext.Windows.parse_shared_lib(output)
                import_lib = self.context.get_output(
                    os.path.splitext(output)[0] + ".lib", deps_local)
            else:
                module_type = ModuleTypes.executable
                descr = os_ext.Windows.parse_executable(output)

            output = self.context.get_output(output, deps_local)
            module_name = descr.get("module_name")
            name = descr.get("target_name")

            LinkerParser.process_namespace(self, namespace)

            targets.append(
                get_module_target(
                    module_type,
                    name,
                    output,
                    msvc_import_lib=import_lib,
                    module_name=module_name,
                    compile_flags=namespace.compile_flags,
                    dependencies=deps_local,
                    include_dirs=namespace.include_dirs,
                    link_flags=namespace.link_flags,
                    objects=objects,
                    libs=libs,
                    sources=sources,
                ))
        return targets
示例#23
0
class Objcopy(ParserBase):
    def __init__(self, context, platform=None):
        ParserBase.__init__(self, context)

        self.platform = get_platform(platform)
        self.program_re = self.platform.get_program_path_re("objcopy")

        # https://sourceware.org/binutils/docs/binutils/objcopy.html
        self.parser = ArgumentParserEx()
        self.parser.set(dest=None, raw_dest="args")
        self.parser.add_argument("--redefine-syms",
                                 raw_handler=self.input_file_after_equals_sign)
        self.parser.add_argument("--keep-symbols",
                                 raw_handler=self.input_file_after_equals_sign)
        self.parser.add_argument("--strip-symbols",
                                 raw_handler=self.input_file_after_equals_sign)
        self.parser.add_argument("--strip-unneeded-symbols",
                                 raw_handler=self.input_file_after_equals_sign)
        self.parser.add_argument("--keep-global-symbols",
                                 raw_handler=self.input_file_after_equals_sign)
        self.parser.add_argument("--keep-global-symbols",
                                 raw_handler=self.input_file_after_equals_sign)
        self.parser.add_argument("--localize-symbols",
                                 raw_handler=self.input_file_after_equals_sign)
        self.parser.add_argument("--globalize-symbols",
                                 raw_handler=self.input_file_after_equals_sign)
        self.parser.add_argument("--weaken-symbols",
                                 raw_handler=self.input_file_after_equals_sign)
        obj_re = re.compile(r"^[^-=+:]+$")
        self.parser.add_argument("infile",
                                 dest="infile",
                                 raw_handler=self.input_file,
                                 args_regexp=obj_re)
        self.parser.add_argument(
            "outfile",
            dest="outfile",
            nargs="?",
            raw_handler=self.output_file,
            args_regexp=obj_re,
        )

    def parse(self, target):
        tokens = target.get("tokens") or []
        if not tokens:
            return target

        if not self.program_re.match(tokens[0]):
            return target

        tokens.pop(0)
        namespace, _ = self.parser.parse_known_args(tokens,
                                                    unknown_dest="args")
        inplace = False
        if namespace.outfile:
            if namespace.outfile[0] == namespace.infile:
                inplace = True
        else:
            inplace = True
        if inplace:
            namespace.infile = self.context.get_file_arg(namespace.infile)
            infile_target = self.context.target_index.get(namespace.infile)
            if infile_target and infile_target["type"] == "module":
                if infile_target.get("post_build_commands") is None:
                    infile_target["post_build_commands"] = []
                infile_target["post_build_commands"].append({
                    "program":
                    "objcopy",
                    "args":
                    namespace.args
                })
                infile_target["dependencies"].extend(namespace.dependencies)
                infile_target["dependencies"].remove(infile_target["output"])
                self.context.update_target(infile_target)
                return []
        return get_command_target(
            None,
            program="objcopy",
            args=namespace.args,
            output=namespace.output,
            dependencies=namespace.dependencies,
        )
示例#24
0
    def test_nargs(self):
        parser = ArgumentParserEx()
        parser.add_argument("--attr2", action="store", nargs="1")
        parser.add_argument("attr1", action="store", nargs="1")

        ns, remaining = parser.parse_known_args(
            ["--attr2", "val1", "val2", "--attr2", "val3"])
        self.assertListEqual([], remaining)
        self.assertEqual(["val2"], ns.attr1)
        self.assertEqual(["val3"], ns.attr2)

        parser = ArgumentParserEx()
        parser.add_argument("--attr2", action="store", nargs="2")
        parser.add_argument("attr1", action="store", nargs="2")

        ns, remaining = parser.parse_known_args(
            ["--attr2", "val1", "val2", "val3", "val4"])
        self.assertListEqual([], remaining)
        self.assertEqual(["val3", "val4"], ns.attr1)
        self.assertEqual(["val1", "val2"], ns.attr2)

        ns, remaining = parser.parse_known_args(
            ["val1", "val2", "--attr2", "val3", "val4", "val5"])
        self.assertListEqual(["val5"], remaining)
        self.assertEqual(["val1", "val2"], ns.attr1)
        self.assertEqual(["val3", "val4"], ns.attr2)

        ns, remaining = parser.parse_known_args(["val1", "--attr2", "val2"])
        self.assertListEqual(["--attr2"], remaining)
        self.assertEqual(["val1", "val2"], ns.attr1)
        self.assertIsNone(vars(ns).get("attr2"))

        ns, remaining = parser.parse_known_args(
            ["val1", "--attr2", "val2", "val3", "val4"])
        self.assertListEqual([], remaining)
        self.assertEqual(["val1", "val4"], ns.attr1)
        self.assertEqual(["val2", "val3"], ns.attr2)

        ns, remaining = parser.parse_known_args(
            ["val1", "--attr2", "val2", "val3"])
        self.assertListEqual([], remaining)
        self.assertEqual(["val1"], ns.attr1)
        self.assertEqual(["val2", "val3"], ns.attr2)

        parser = ArgumentParserEx()
        parser.add_argument("attr1", action="store", nargs="?")
        parser.add_argument("--attr2", action="store", nargs="?")

        ns, remaining = parser.parse_known_args(["val1", "--attr2"])
        self.assertListEqual([], remaining)
        self.assertEqual(["val1"], ns.attr1)
        self.assertEqual([], ns.attr2)

        ns, remaining = parser.parse_known_args(["--attr2", "val1"])
        self.assertListEqual([], remaining)
        self.assertEqual(None, ns.attr1)
        self.assertEqual(["val1"], ns.attr2)

        parser = ArgumentParserEx()
        parser.add_argument("--attr2", action="store", nargs="+")
        parser.add_argument("attr1", action="store", nargs="+")

        ns, remaining = parser.parse_known_args(["val1", "val2", "--attr2"])
        self.assertListEqual(["--attr2"], remaining)
        self.assertEqual(["val1", "val2"], ns.attr1)
        self.assertIsNone(vars(ns).get("attr2"))

        ns, remaining = parser.parse_known_args(["--attr2", "val1", "val2"])
        self.assertListEqual([], remaining)
        self.assertIsNone(vars(ns).get("attr1"))
        self.assertEqual(["val1", "val2"], ns.attr2)

        ns, remaining = parser.parse_known_args(
            ["val1", "val2", "--attr2", "val3"])
        self.assertListEqual([], remaining)
        self.assertEqual(["val1", "val2"], ns.attr1)
        self.assertEqual(["val3"], ns.attr2)

        parser = ArgumentParserEx()
        parser.add_argument("attr1", action="store", nargs="*")
        parser.add_argument("--attr2", action="store", nargs="*")

        ns, remaining = parser.parse_known_args(["val1", "val2", "--attr2"])
        self.assertListEqual([], remaining)
        self.assertEqual(["val1", "val2"], ns.attr1)
        self.assertEqual([], ns.attr2)

        ns, remaining = parser.parse_known_args(["--attr2", "val1", "val2"])
        self.assertListEqual([], remaining)
        self.assertEqual([], ns.attr1)
        self.assertEqual(["val1", "val2"], ns.attr2)

        ns, remaining = parser.parse_known_args(["val1", "--attr2", "val2"])
        self.assertListEqual([], remaining)
        self.assertEqual(["val1"], ns.attr1)
        self.assertEqual(["val2"], ns.attr2)

        parser = ArgumentParserEx()
        parser.add_argument("-C", action="append", nargs="?")
        ns = parser.parse_args(["-C", "a1", "-C"])
        self.assertListEqual([["a1"], []], ns.C)

        parser = ArgumentParserEx()
        parser.add_argument("-C", action="append", nargs="*")
        ns = parser.parse_args(["-C", "a1", "-C", "b1", "b2", "-C"])
        self.assertListEqual([["a1"], ["b1", "b2"], []], ns.C)

        parser = ArgumentParserEx()
        parser.add_argument("-C", action="append", nargs="+")
        ns = parser.parse_args(["-C", "a1", "-C", "b1", "b2"])
        self.assertListEqual([["a1"], ["b1", "b2"]], ns.C)

        parser = ArgumentParserEx()
        parser.add_argument("C", action="append", nargs="?")
        ns = parser.parse_args(["1"])
        self.assertListEqual([["1"]], ns.C)
        ns, remaining = parser.parse_known_args(["1", "2"])
        self.assertListEqual(["2"], remaining)
        self.assertListEqual([["1"]], ns.C)
        ns = parser.parse_args([])
        self.assertIsNone(ns.C)

        parser = ArgumentParserEx()
        parser.add_argument("C", action="append", nargs="*")
        ns = parser.parse_args(["1"])
        self.assertListEqual([["1"]], ns.C)
        ns = parser.parse_args(["1", "2"])
        self.assertListEqual([["1", "2"]], ns.C)
        ns, _remaining = parser.parse_known_args(["1", "2", "-a", "3"])
        self.assertListEqual([["1", "2"], ["3"]], ns.C)
        ns, _remaining = parser.parse_known_args(["1", "2", "-a"])
        self.assertListEqual([["1", "2"]], ns.C)
        # Check that empty list does not raise an Exception
        parser.parse_args([])

        parser = ArgumentParserEx()
        parser.add_argument("C", action="append", nargs="+")
        ns = parser.parse_args(["1"])
        self.assertListEqual([["1"]], ns.C)
        ns = parser.parse_args(["1", "2"])
        self.assertListEqual([["1", "2"]], ns.C)
        ns, _remaining = parser.parse_known_args(["1", "2", "-a", "3"])
        self.assertListEqual([["1", "2"], ["3"]], ns.C)
        ns, _remaining = parser.parse_known_args(["1", "2", "-a"])
        self.assertListEqual([["1", "2"]], ns.C)
        self.assertRaisesRegexp(ValueError, "Required attribute not found",
                                parser.parse_args, [])
示例#25
0
class Clang_Gcc(CompilerParser, LinkerParser):
    asm_exts = [".s", ".S"]
    c_exts = [".c", ".m"]
    cpp_exts = [".cc", ".cpp", ".cxx", ".mm"]
    source_exts = asm_exts + c_exts + cpp_exts

    class Mode:
        link = "link"
        assemble = "assemble"  # don't link
        compile = "compile"  # don't assemble & link
        preprocess = "preprocess"  # don't compile, assemble & link

    class WholeArchive:
        enable = "--whole-archive"
        disable = "--no-whole-archive"

    class LinkType:
        static = "-Bstatic"
        dynamic = "-Bdynamic"

    priority = 7

    @staticmethod
    def add_arguments(arg_parser):
        CompilerParser.add_arguments(arg_parser)
        LinkerParser.add_arguments(arg_parser)

    def __init__(
        self,
        context,
        project_version=None,
        platform=platform.system().lower(),
        ignore_link_flags=None,
        ignore_compile_flags=None,
    ):
        CompilerParser.__init__(self,
                                context,
                                ignore_compile_flags=ignore_compile_flags)
        LinkerParser.__init__(self,
                              context,
                              ignore_link_flags=ignore_link_flags)

        self.platform_name = platform
        self.platform = get_platform(platform)
        self.project_version = project_version
        self.program_re = self.platform.get_program_path_re(
            "cc", "c++", "clang", "clang++", "gcc", "g++")

        # Clang/GCC arguments
        # See https://linux.die.net/man/1/gcc
        # https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html
        # https://gcc.gnu.org/onlinedocs/gcc/Invoking-GCC.html
        # https://clang.llvm.org/docs/ClangCommandLineReference.html
        self.parser = ArgumentParserEx(prog="gcc")
        self.parser.set_defaults(
            mode=self.Mode.link,
            lib_dirs=[],
            libs=[],
            include_dirs=[],
            link_flags=[],
            compile_flags=[],
        )

        # control flags
        self.parser.set(dest=None)
        self.parser.add_argument("-E",
                                 action="store_const",
                                 const=self.Mode.preprocess,
                                 dest="mode")
        self.parser.add_argument("-MD", action="store_true")
        self.parser.add_argument("-MF")
        self.parser.add_argument("-MMD", action="store_true")
        self.parser.add_argument("-MP", action="store_true")
        self.parser.add_argument("-MT")
        self.parser.add_argument("-S",
                                 action="store_const",
                                 const=self.Mode.compile,
                                 dest="mode")
        self.parser.add_argument("-c",
                                 action="store_const",
                                 const=self.Mode.assemble,
                                 dest="mode")
        self.parser.add_argument("-o", dest="output")
        self.parser.add_argument("-pipe", action="store_true")

        # linker flags
        self.parser.set(raw_dest="link_flags")
        self.parser.add_argument("-L",
                                 action="append",
                                 dest="lib_dirs",
                                 raw_dest=None)
        self.parser.add_argument("-Q", dest="driver_arguments")
        self.parser.add_argument(prefixes=["-Wl,"])
        self.parser.add_argument("-all_load", action="store_true")
        self.parser.add_argument("-compatibility_version")
        self.parser.add_argument("-current_version")
        self.parser.add_argument("-version-info")
        self.parser.add_argument(
            "-dynamiclib",
            "-dynamic",
            action="store_true",
            raw_dest=None,
            dest="is_shared",
        )
        self.parser.add_argument("-exported_symbols_list")
        self.parser.add_argument("-framework")
        self.parser.add_argument("-rpath")
        self.parser.add_argument("-headerpad_max_install_names",
                                 action="store_true")
        self.parser.add_argument("-no-undefined", action="store_true")
        self.parser.add_argument("-install_name")
        self.parser.add_argument(prefixes=["-l:", "-l"])
        self.parser.add_argument("-nolibc", action="store_true")
        self.parser.add_argument("-nostdlib++", action="store_true")
        self.parser.add_argument("-no-canonical-prefixes", action="store_true")
        self.parser.add_argument("-nostdlib", action="store_true")
        self.parser.add_argument("-single_module", action="store_true")
        self.parser.add_argument("-pie", action="store_true")
        self.parser.add_argument("-rdynamic", action="store_true")
        self.parser.add_argument("-shared",
                                 action="store_true",
                                 raw_dest=None,
                                 dest="is_shared")
        self.parser.add_argument("-static", action="store_true")
        self.parser.add_argument("-static-libgcc", action="store_true")
        self.parser.add_argument("-static-libstdc++", action="store_true")
        self.parser.add_argument("-stdlib")
        self.parser.add_argument(flags=["-z"])
        self.parser.add_argument("static_libs",
                                 nargs="*",
                                 args_regexp=re.compile(r"^(?!-Wl).+\.a$"))
        if platform == "linux":
            self.parser.add_argument(
                "shared_libs",
                nargs="*",
                args_regexp=re.compile(r"^(?!-Wl).+\.so$"))
        if platform == "darwin":
            self.parser.add_argument(
                "shared_libs",
                nargs="*",
                args_regexp=re.compile(r"^(?!-Wl).+\.dylib(?:\.\d+)*$"),
            )

        # compiler flags
        self.parser.set(raw_dest="compile_flags")
        self.parser.add_argument("-D", raw_format=format_flag_gnu)
        self.parser.add_argument(prefixes=["-B"])  # Aurora stuff
        self.parser.add_argument("--target", "-target")
        self.parser.add_argument("--gcc-toolchain", "-gcc-toolchain")
        self.parser.add_argument("--sysroot")
        self.parser.add_argument("-I",
                                 action="append",
                                 dest="include_dirs",
                                 raw_dest=None)
        self.parser.add_argument(prefixes=["-O"])
        self.parser.add_argument("-Q")
        self.parser.add_argument("-U", raw_format=format_flag_gnu)
        self.parser.add_argument(prefixes=["-W"],
                                 args_regexp=re.compile("^(?!l,)"))
        self.parser.add_argument(prefixes=["-Wa,"])
        self.parser.add_argument("-arch")
        self.parser.add_argument("-w", action="store_true")
        self.parser.add_argument(prefixes=["-f"])
        self.parser.add_argument(prefixes=["-W"])
        self.parser.add_argument("-W", action="store_true")
        self.parser.add_argument("-pedantic", action="store_true")
        self.parser.add_argument(prefixes=["-std=", "--std="])
        self.parser.add_argument("-x", dest="language_mode")
        self.parser.add_argument("-isystem", action="append")

        # compiler + linker flags
        # TODO: HACK: list support in raw_dest is temporary until a better solution comes up
        self.parser.set(raw_dest=["compile_flags", "link_flags"])
        self.parser.add_argument("-g", nargs="?")
        self.parser.add_argument("-isysroot")
        self.parser.add_argument(prefixes=["-m"])
        self.parser.add_argument("-pthread", action="store_true")

        self.parser.add_argument("infiles",
                                 nargs="*",
                                 dest="infiles",
                                 raw_dest=None)

    # Split compile flag list with multiple -arch flags into multiple lists,
    # each with only one -arch.
    # This fixes clang preprocessor error: cannot use 'dependencies' output with multiple -arch options
    def _split_compile_flags_for_multiarch(self, compile_flags):
        arch_args = {
            idx: arg
            for idx, arg in enumerate(compile_flags)
            if isinstance(arg, list) and arg[0] == "-arch"
        }
        if len(arch_args) > 1:
            compile_flags = [
                arg for idx, arg in enumerate(compile_flags)
                if idx not in arch_args
            ]
            result = [compile_flags]
            for _ in range(0, len(arch_args) - 1):
                result.append(deepcopy(result[0]))
            compile_flags_iter = iter(result)
            for idx, arg in arch_args.items():
                next(compile_flags_iter).insert(idx, arg)
            return result
        else:
            return [compile_flags]

    def _add_implicit_dependencies(
        self,
        compiler,
        dependencies,
        compile_flags,
        include_dirs,
        sources,
        target_platform,
        cwd,
    ):
        if not sources:
            return

        include_dir_args = ["-I" + d for d in include_dirs]
        sources = [s for s in sources]

        host_system = platform.system().lower()
        if compiler.find("clang") != -1:
            if host_system == "windows" and target_platform != host_system:
                # to avoid errors like:
                # * clang++.exe: error: unsupported option '-fPIC' for target 'x86_64-pc-windows-msvc'
                if target_platform == "linux":
                    compile_flags = compile_flags + [
                        "--target=i686-pc-linux-gnu"
                    ]
                if target_platform == "darwin":
                    compile_flags = compile_flags + [
                        "--target=i686-apple-darwin10"
                    ]

        implicit_dependencies = []
        implicit_dependencies_set = set()
        for compile_flags in self._split_compile_flags_for_multiarch(
                compile_flags):
            cmd = ([compiler, "-M"] + flatten_list(compile_flags) +
                   include_dir_args + sources)
            p = subprocess.Popen(cmd,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE,
                                 cwd=cwd)
            stdout, stderr = p.communicate()
            if type(stdout) is not str:
                stdout = stdout.decode("utf-8", "replace")
            if type(stderr) is not str:
                stderr = stderr.decode("utf-8", "replace")
            retcode = p.poll()
            if retcode:
                cmd_str = " ".join(cmd)
                logger.error(
                    "Command '{}' returned non-zero exit code {}\n{}".format(
                        cmd_str, retcode, stderr))

            for line in stdout.splitlines():
                files = line.rstrip("\\").lstrip().split(" ")
                for f in files:
                    if not f or f.endswith(":") or f in sources:
                        continue
                    if f not in implicit_dependencies_set:
                        implicit_dependencies.append(f)
                        implicit_dependencies_set.add(f)

        return [
            self.context.get_file_arg(self.context.normalize_path(dep),
                                      dependencies)
            for dep in implicit_dependencies
        ]

    # filter libs from linker flags
    def _filter_lib_args(self, ns, dependencies):
        libs = []
        remaining_flags = []
        whole_archive = False
        static_only = False
        for arg in ns.link_flags:
            skip = False
            is_lib = False
            if not isinstance(arg, str):
                pass
            elif arg.endswith(self.WholeArchive.enable):
                whole_archive = True
                skip = True
            elif arg.endswith(self.WholeArchive.disable):
                whole_archive = False
                skip = True
            elif arg.endswith(self.LinkType.static):
                static_only = True
                skip = True
            elif arg.endswith(self.LinkType.dynamic):
                static_only = False
                skip = True
            else:
                if arg.startswith("-l"):
                    arg = arg[2:]
                    if arg[0] == ":":
                        arg = self.context.get_file_arg(arg[1:], dependencies)
                    else:
                        arg = self.context.get_lib_arg(arg,
                                                       dependencies,
                                                       ns.lib_dirs,
                                                       static_only=static_only)
                    is_lib = True
                    skip = True
                elif arg.startswith("-Wl,"):
                    pass
                elif self.platform.is_static_lib(
                        arg) or self.platform.is_shared_lib(arg):
                    arg = self.context.get_file_arg(arg, dependencies)
                    is_lib = True
                    skip = True

            if is_lib:
                if whole_archive:
                    libs.append({"gcc_whole_archive": True, "value": arg})
                else:
                    libs.append(arg)

            if not skip:
                remaining_flags.append(arg)

        ns.link_flags = remaining_flags
        ns.libs.extend(libs)

    _capture_next_arg = [
        "-Wl,-soname",
        "-Wl,-compatibility_version",
        "-Wl,-current_version",
        "-Wl,-z",
        "-Wl,-version-script",
        "-Wl,--version-script",
        "-Wl,-rpath",
        "-Wl,--rpath",
    ]
    _file_arg = {
        "-exported_symbols_list": "",
        "-Wl,-version-script": "-Wl,",
        "-Wl,--version-script": "-Wl,",
    }
    _dir_arg = {
        "-Wl,-rpath": "-Wl,",
        "-Wl,--rpath": "-Wl,",
        "-rpath": "",
    }
    _arg_set = set(_file_arg.keys()) | set(_dir_arg.keys())

    def _process_link_flags(self, flags, dependencies):
        result = []
        it = iter(flags)
        for f in it:
            if isinstance(f, str):
                if f in self._capture_next_arg:
                    f = [f, next(it)]
                else:
                    for s in self._arg_set:
                        if f.startswith(s):
                            delim = f[len(s)]
                            tmp = f.split(delim)
                            if s in self._file_arg:
                                tmp[-1] = self.context.get_file_arg(
                                    tmp[-1], dependencies)
                            else:
                                tmp[-1] = self.context.get_dir_arg(
                                    tmp[-1], dependencies)
                            f = delim.join(tmp)
                            continue

            if isinstance(f, list):
                prefix = None
                get_arg_func = None
                if f[0] in self._file_arg:
                    prefix = self._file_arg[f[0]]
                    get_arg_func = self.context.get_file_arg
                if f[0] in self._dir_arg:
                    prefix = self._dir_arg[f[0]]
                    get_arg_func = self.context.get_dir_arg
                if get_arg_func:
                    if not f[-1].startswith(prefix):
                        prefix = ""
                    else:
                        f[-1] = f[-1][len(prefix):]
                    f[-1] = prefix + get_arg_func(f[-1], dependencies)
            result.append(f)
        return result

    def parse(self, target):
        tokens = target.get("tokens")
        if not tokens:
            return target

        # .strip('{}$') is a workaround for paths like ${LDCMD:-gcc}
        # TODO: parameter expansion parser: http://wiki.bash-hackers.org/syntax/pe
        if not self.program_re.match(tokens[0].strip("{}$")):
            return target

        gcc = tokens.pop(0)

        gcc_nrm = self.context.platform.normalize_path(gcc)
        if gcc_nrm in self.context.path_aliases:
            gcc = self.context.path_aliases[gcc_nrm]
        namespace, _ = self.parser.parse_known_args(
            tokens, unknown_dest=["compile_flags", "link_flags"])
        if namespace.mode not in [self.Mode.link, self.Mode.assemble]:
            return target

        if namespace.mode != self.Mode.link:
            namespace.link_flags = []
            namespace.lib_dirs = []
            namespace.libs = []

        dependencies = []
        namespace.lib_dirs = list(
            map(
                lambda p: p[2:],
                filter_flags(self.ignore_link_flags_rxs,
                             ["-L" + d for d in namespace.lib_dirs]),
            ))
        LinkerParser.process_namespace(self, namespace)
        self._filter_lib_args(namespace, dependencies)
        namespace.link_flags = self._process_link_flags(
            namespace.link_flags, dependencies)

        original_srcs = []
        objects = []
        sources = []
        for infile in namespace.infiles:
            infile = self.context.normalize_path(infile)
            assert not (self.platform.is_static_lib(infile)
                        or self.platform.is_shared_lib(infile)), infile
            language = None
            ext = os.path.splitext(infile)[1]
            if ext in self.c_exts:
                language = "C"
            elif ext in self.cpp_exts:
                language = "C++"
            elif ext in self.asm_exts:
                language = "GASM"

            if language is None:
                objects.append(self.context.get_file_arg(infile, dependencies))
            else:
                original_srcs.append(infile)
                sources.append(
                    get_source_file_reference(
                        self.context.get_file_arg(infile, dependencies),
                        language))

        if not sources:
            # Ignore compiler flags if no source files specified
            namespace.compile_flags = []
            namespace.include_dirs = []

        CompilerParser.process_namespace(self, namespace)

        include_dirs_not_relocatable = namespace.include_dirs

        namespace.include_dirs = [
            self.context.get_dir_arg(self.context.normalize_path(d),
                                     dependencies)
            for d in include_dirs_not_relocatable
        ]

        if namespace.mode == self.Mode.assemble:
            if namespace.output is None:
                namespace.output = (os.path.basename(
                    os.path.splitext(namespace.infiles[0])[0]) + ".o")
            module_type = ModuleTypes.object_lib
            name = None
            module_name = None
            version = None
        elif namespace.mode == self.Mode.link:
            if namespace.output is None:
                namespace.output = "a.out"
            descr = None
            if namespace.is_shared:
                descr = self.platform.parse_shared_lib(namespace.output)
                module_type = ModuleTypes.shared_lib
            else:
                descr = self.platform.parse_executable(namespace.output)
                module_type = ModuleTypes.executable
            if descr:
                module_name = descr["module_name"]
                name = descr["target_name"]
                version = descr["version"] or self.project_version
            else:
                module_name = None
                name = None
                version = self.project_version

        namespace.output = self.context.normalize_path(namespace.output)
        self._add_implicit_dependencies(
            gcc,
            dependencies,
            namespace.compile_flags,
            include_dirs_not_relocatable,
            original_srcs,
            self.platform_name,
            self.context.working_dir,
        )
        namespace.output = self.context.get_output(namespace.output,
                                                   dependencies)

        target = get_module_target(
            module_type,
            name,
            namespace.output,
            compile_flags=namespace.compile_flags,
            dependencies=dependencies,
            module_name=module_name,
            include_dirs=namespace.include_dirs,
            link_flags=namespace.link_flags,
            libs=namespace.libs,
            objects=objects,
            sources=sources,
            version=version,
        )

        return target
示例#26
0
 def test_multicharacter_short_flag_disables_suffix_value(self):
     parser = ArgumentParserEx()
     parser.add_argument("-Xclang")
     ns, remaining = parser.parse_known_args(["-Xclangvalue"])
     self.assertListEqual(["-Xclangvalue"], remaining)
     self.assertIsNone(vars(ns).get("Xclang"))