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
« 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/>.
20import argparse
21import os
22import sys
23from dataclasses import dataclass
24from typing import Iterable, Optional, Sequence
26from yamale import YamaleError
28from trlc.errors import Message_Handler, TRLC_Error
29from trlc.trlc import Source_Manager
31from lobster.common.errors import PathError
32from lobster.common.items import Requirement
33from lobster.common.multi_file_input_tool import create_worklist, MultiFileInputTool
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
46@dataclass
47class TrlcToolConfig:
48 config: str
49 dir_or_files: Sequence[str] = ()
50 out: str = "lobster-trlc.lobster"
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 )
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 )
109 return 1
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}'")
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")
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)
149 # lobster-trace: trlc_req.Output_File
150 self._write_output(Requirement, out_file, items)
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")
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 )
165def main(args: Optional[Sequence[str]] = None) -> int:
166 return LOBSTER_Trlc().run(args)