Coverage for lobster/tools/trlc/trlc_tool.py: 0%

73 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2026-08-05 10:00 +0000

1#!/usr/bin/env python3 

2# 

3# LOBSTER - Lightweight Open BMW Software Traceability Evidence Report 

4# Copyright (C) 2023-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 

5# 

6# This program is free software: you can redistribute it and/or modify 

7# it under the terms of the GNU Affero General Public License as 

8# published by the Free Software Foundation, either version 3 of the 

9# License, or (at your option) any later version. 

10# 

11# This program is distributed in the hope that it will be useful, but 

12# WITHOUT ANY WARRANTY; without even the implied warranty of 

13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 

14# Affero General Public License for more details. 

15# 

16# You should have received a copy of the GNU Affero General Public 

17# License along with this program. If not, see 

18# <https://www.gnu.org/licenses/>. 

19 

20import argparse 

21import os 

22import sys 

23from dataclasses import dataclass 

24from typing import Iterable, Optional, Sequence 

25 

26from yamale import YamaleError 

27 

28from trlc.errors import Message_Handler, TRLC_Error 

29from trlc.trlc import Source_Manager 

30 

31from lobster.common.errors import PathError 

32from lobster.common.items import Requirement 

33from lobster.common.multi_file_input_tool import create_worklist, MultiFileInputTool 

34 

35from lobster.tools.trlc.converter import Converter 

36from lobster.tools.trlc.errors import ( 

37 InvalidConversionRuleError, 

38 RecordObjectComponentError, 

39 TrlcFailure, 

40 TupleToStringFailedError, 

41 TupleToStringMissingError, 

42) 

43from lobster.tools.trlc.lobster_trlc_config import LobsterTrlcConfig 

44 

45 

46@dataclass 

47class TrlcToolConfig: 

48 config: str 

49 dir_or_files: Sequence[str] = () 

50 out: str = "lobster-trlc.lobster" 

51 

52 

53class LOBSTER_Trlc(MultiFileInputTool): 

54 def __init__(self): 

55 super().__init__( 

56 name = "trlc", 

57 description = "Extract tracing data from TRLC files.", 

58 extensions = ["rsl", "trlc"], 

59 official = True, 

60 ) 

61 

62 def _run_impl(self, options: argparse.Namespace): 

63 try: 

64 self.run_lobster_trlc( 

65 config=LobsterTrlcConfig.from_file(options.config), 

66 dir_or_files=options.dir_or_files, 

67 out_file=options.out, 

68 ) 

69 return 0 

70 except YamaleError as e: 

71 print( 

72 f"{self.name}: The configuration file does not " 

73 f"conform to the YAML schema. {e}", 

74 file=sys.stderr, 

75 ) 

76 except TRLC_Error as e: 

77 print( 

78 f"{self.name}: An error occurred during processing: {e}", 

79 file=sys.stderr, 

80 ) 

81 except FileNotFoundError as e: 

82 print( 

83 f"{self.name}: File or directory not found: {e}", 

84 file=sys.stderr, 

85 ) 

86 except PathError as e: 

87 print( 

88 f"{self.name}: {e}", 

89 file=sys.stderr, 

90 ) 

91 except TrlcFailure as e: 

92 print( 

93 f"{self.name}: TRLC processing failed: {e}", 

94 file=sys.stderr, 

95 ) 

96 except (InvalidConversionRuleError, RecordObjectComponentError) as e: 

97 print( 

98 f"{self.name}: Invalid conversion rule defined in {options.config}: " 

99 f"{e}", 

100 file=sys.stderr, 

101 ) 

102 except (TupleToStringMissingError, TupleToStringFailedError) as e: 

103 print( 

104 f"{self.name}: error in 'to-string-rules' in {options.config}: " 

105 f"{e}", 

106 file=sys.stderr, 

107 ) 

108 

109 return 1 

110 

111 @staticmethod 

112 def _register_trlc_files(sm: Source_Manager, work_list: Iterable[str]) -> None: 

113 for item in work_list: 

114 ok = True 

115 if os.path.isfile(item): 

116 ok = sm.register_file(item) 

117 elif os.path.isdir(item): 

118 ok = sm.register_directory(item) 

119 else: 

120 raise FileNotFoundError(item) 

121 if not ok: 

122 raise PathError(f"Failed to register file or directory '{item}'") 

123 

124 def run_lobster_trlc( 

125 self, 

126 config: LobsterTrlcConfig, 

127 dir_or_files: Sequence[str], 

128 out_file: str, 

129 ) -> None: 

130 work_list = create_worklist(config, list(dir_or_files)) 

131 trlc_mh = Message_Handler() 

132 sm = Source_Manager(trlc_mh) 

133 self._register_trlc_files(sm, work_list) 

134 symbol_table = sm.process() 

135 if not symbol_table: 

136 raise TrlcFailure("aborting due to TRLC error") 

137 

138 items = [] 

139 converter = Converter( 

140 conversion_rules=config.conversion_rules, 

141 to_string_rules=config.to_string_rules, 

142 symbol_table=symbol_table, 

143 ) 

144 for n_obj in symbol_table.iter_record_objects(): 

145 item = converter.generate_lobster_object(n_obj) 

146 if item: 

147 items.append(item) 

148 

149 # lobster-trace: trlc_req.Output_File 

150 self._write_output(Requirement, out_file, items) 

151 

152 

153def lobster_trlc(config: TrlcToolConfig) -> None: 

154 """This is an API function.""" 

155 if not config.config: 

156 raise ValueError("config must not be empty") 

157 

158 LOBSTER_Trlc().run_lobster_trlc( 

159 config=LobsterTrlcConfig.from_file(config.config), 

160 dir_or_files=config.dir_or_files, 

161 out_file=config.out, 

162 ) 

163 

164 

165def main(args: Optional[Sequence[str]] = None) -> int: 

166 return LOBSTER_Trlc().run(args)