def lang(self: ArgsProtocol) -> Lang: """ Returns: A `blight.enums.Lang` value representing the tool's language """ x_lang_map = { "c": Lang.C, "c-header": Lang.C, "c++": Lang.Cxx, "c++-header": Lang.Cxx } # First, check for `-x lang`. This overrides the language determined by # the frontend's binary name (e.g. `g++`). x_flag_index = rindex_prefix(self.args, "-x") if x_flag_index is not None: if self.args[x_flag_index] == "-x": # TODO(ww): Maybe bounds check. x_lang = self.args[x_flag_index + 1] else: # NOTE(ww): -xc and -xc++ both work, at least on GCC. x_lang = self.args[x_flag_index][2:] return x_lang_map.get(x_lang, Lang.Unknown) # No `-x lang` means that we're operating in the frontend's default mode. if self.__class__ == CC: return Lang.C elif self.__class__ == CXX: return Lang.Cxx else: logger.debug( f"unknown default language mode for {self.__class__.__name__}") return Lang.Unknown
def outputs(self) -> List[str]: """ Specializes `Tool.outputs` for the linker. """ outputs = super().outputs if outputs != []: return outputs # The GNU linker additionally supports --output=OUTFILE and # --output OUTFILE. Handle them here. output_flag_index = rindex_prefix(self.args, "--output") if output_flag_index is None: return ["a.out"] # Split option form. if self.args[output_flag_index] == "--output": return [self.args[output_flag_index + 1]] # Assignment form. return [self.args[output_flag_index].split("=")[1]]
def outputs(self) -> List[str]: """ Returns all "outputs" produced by the tool. "Outputs" is subjectively defined to be the "main" products of a tool, i.e. results of a particular stage or invocation and **not** any incidental or metadata files that might otherwise be created in the process. Tools may further refine the behavior of this mixin-supplied property by overriding it with their own, more specific behavior. Returns: A list of `str`, each of which is an output """ o_flag_index = rindex_prefix(self.args, "-o") if o_flag_index is None: return [] if self.args[o_flag_index] == "-o": return [self.args[o_flag_index + 1]] # NOTE(ww): Outputs like -ofoo. Gross, but valid according to GCC. return [self.args[o_flag_index][2:]]
def std(self: LangProtocol) -> Std: """ Returns: A `blight.enums.Std` value representing the tool's standard """ # First, a special case: if -ansi is present, we're in # C89 mode for C code and C++03 mode for C++ code. if "-ansi" in self.args: if self.lang == Lang.C: return Std.C89 elif self.lang == Lang.Cxx: return Std.Cxx03 else: logger.debug(f"-ansi passed but unknown language: {self.lang}") return Std.Unknown # Experimentally, both GCC and clang respect the last -std=XXX flag passed. # See: https://stackoverflow.com/questions/40563269/passing-multiple-std-switches-to-g std_flag_index = rindex_prefix(self.args, "-std=") # No -std=XXX flags? The tool is operating in its default standard mode, # which is determined by its language. if std_flag_index is None: if self.lang == Lang.C: return Std.GnuUnknown elif self.lang == Lang.Cxx: return Std.GnuxxUnknown else: logger.debug( f"no -std= flag and unknown language: {self.lang}") return Std.Unknown last_std_flag = self.args[std_flag_index] std_flag_map = { # C89 flags. "-std=c89": Std.C89, "-std=c90": Std.C89, "-std=iso9899:1990": Std.C89, # C94 flags. "-std=iso9899:199409": Std.C94, # C99 flags. "-std=c99": Std.C99, "-std=c9x": Std.C99, "-std=iso9899:1999": Std.C99, "-std=iso9899:199x": Std.C99, # C11 flags. "-std=c11": Std.C11, "-std=c1x": Std.C11, "-std=iso9899:2011": Std.C11, # C17 flags. "-std=c17": Std.C17, "-std=c18": Std.C17, "-std=iso9899:2017": Std.C17, "-std=iso9899:2018": Std.C17, # C20 (presumptive) flags. "-std=c2x": Std.C2x, # GNU89 flags. "-std=gnu89": Std.Gnu89, "-std=gnu90": Std.Gnu89, # GNU99 flags. "-std=gnu99": Std.Gnu99, "-std=gnu9x": Std.Gnu99, # GNU11 flags. "-std=gnu11": Std.Gnu11, "-std=gnu1x": Std.Gnu11, # GNU17 flags. "-std=gnu17": Std.Gnu17, "-std=gnu18": Std.Gnu17, # GNU20 (presumptive) flags. "-std=gnu2x": Std.Gnu2x, # C++03 flags. # NOTE(ww): Both gcc and clang treat C++98 mode as C++03 mode. "-std=c++98": Std.Cxx03, "-std=c++03": Std.Cxx03, # C++11 flags. "-std=c++11": Std.Cxx11, "-std=c++0x": Std.Cxx11, # C++14 flags. "-std=c++14": Std.Cxx14, "-std=c++1y": Std.Cxx14, # C++17 flags. "-std=c++17": Std.Cxx17, "-std=c++1z": Std.Cxx17, # C++20 (presumptive) flags. "-std=c++2a": Std.Cxx2a, # GNU++03 flags. "-std=gnu++98": Std.Gnuxx03, "-std=gnu++03": Std.Gnuxx03, # GNU++11 flags. "-std=gnu++11": Std.Gnuxx11, "-std=gnu++0x": Std.Gnuxx11, # GNU++14 flags. "-std=gnu++14": Std.Gnuxx14, "-std=gnu++1y": Std.Gnuxx14, # GNU++17 flags. "-std=gnu++17": Std.Gnuxx17, "-std=gnu++1z": Std.Gnuxx17, # GNU++20 (presumptive) flags. "-std=gnu++2a": Std.Gnuxx2a, } std = std_flag_map.get(last_std_flag) if std is not None: return std # If we've made it here, then we've reached a -std=XXX flag that we # don't know yet. Make an effort to guess at it. std_name = last_std_flag.split("=")[1] if std_name.startswith("c++"): logger.debug(f"partially unrecognized c++ std: {last_std_flag}") return Std.CxxUnknown elif std_name.startswith("gnu++"): logger.debug(f"partially unrecognized gnu++ std: {last_std_flag}") return Std.GnuxxUnknown elif std_name.startswith("gnu"): logger.debug(f"partially unrecognized gnu c std: {last_std_flag}") return Std.GnuUnknown elif std_name.startswith("c") or std_name.startswith("iso9899"): logger.debug(f"partially unrecognized c std: {last_std_flag}") return Std.CUnknown logger.debug(f"completely unrecognized -std= flag: {last_std_flag}") return Std.Unknown