spec-cleaner 1.2.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. spec_cleaner/__init__.py +170 -0
  2. spec_cleaner/__main__.py +11 -0
  3. spec_cleaner/dependency_parser.py +273 -0
  4. spec_cleaner/fileutils.py +64 -0
  5. spec_cleaner/rpmbuild.py +56 -0
  6. spec_cleaner/rpmcheck.py +77 -0
  7. spec_cleaner/rpmcleaner.py +427 -0
  8. spec_cleaner/rpmcopyright.py +117 -0
  9. spec_cleaner/rpmdescription.py +38 -0
  10. spec_cleaner/rpmexception.py +26 -0
  11. spec_cleaner/rpmfiles.py +116 -0
  12. spec_cleaner/rpmhelpers.py +286 -0
  13. spec_cleaner/rpminstall.py +64 -0
  14. spec_cleaner/rpmpackage.py +34 -0
  15. spec_cleaner/rpmpreamble.py +716 -0
  16. spec_cleaner/rpmpreambleelements.py +367 -0
  17. spec_cleaner/rpmprep.py +83 -0
  18. spec_cleaner/rpmprune.py +26 -0
  19. spec_cleaner/rpmregexp.py +237 -0
  20. spec_cleaner/rpmrequirestoken.py +107 -0
  21. spec_cleaner/rpmscriplets.py +55 -0
  22. spec_cleaner/rpmsection.py +393 -0
  23. spec_cleaner-1.2.3.data/data/lib/obs/service/clean_spec_file +54 -0
  24. spec_cleaner-1.2.3.data/data/lib/obs/service/clean_spec_file.service +8 -0
  25. spec_cleaner-1.2.3.data/data/lib/obs/service/format_spec_file +54 -0
  26. spec_cleaner-1.2.3.data/data/lib/obs/service/format_spec_file.service +8 -0
  27. spec_cleaner-1.2.3.data/data/share/spec-cleaner/allowed_groups.txt +245 -0
  28. spec_cleaner-1.2.3.data/data/share/spec-cleaner/cmake_conversions.txt +394 -0
  29. spec_cleaner-1.2.3.data/data/share/spec-cleaner/excludes-bracketing.txt +185 -0
  30. spec_cleaner-1.2.3.data/data/share/spec-cleaner/licenses-suse.toml +1674 -0
  31. spec_cleaner-1.2.3.data/data/share/spec-cleaner/licenses.toml +1648 -0
  32. spec_cleaner-1.2.3.data/data/share/spec-cleaner/licenses_changes.txt +1643 -0
  33. spec_cleaner-1.2.3.data/data/share/spec-cleaner/licenses_exceptions.txt +567 -0
  34. spec_cleaner-1.2.3.data/data/share/spec-cleaner/perl_conversions.txt +1381 -0
  35. spec_cleaner-1.2.3.data/data/share/spec-cleaner/pkgconfig_conversions.txt +2021 -0
  36. spec_cleaner-1.2.3.data/data/share/spec-cleaner/tex_conversions.txt +2800 -0
  37. spec_cleaner-1.2.3.dist-info/AUTHORS +8 -0
  38. spec_cleaner-1.2.3.dist-info/COPYING +26 -0
  39. spec_cleaner-1.2.3.dist-info/METADATA +27 -0
  40. spec_cleaner-1.2.3.dist-info/RECORD +43 -0
  41. spec_cleaner-1.2.3.dist-info/WHEEL +5 -0
  42. spec_cleaner-1.2.3.dist-info/entry_points.txt +2 -0
  43. spec_cleaner-1.2.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,170 @@
1
+ # vim: set ts=4 sw=4 et: coding=UTF-8
2
+
3
+ # Copyright (c) 2015, SUSE LINUX Products GmbH, Nuernberg, Germany
4
+ # All rights reserved.
5
+ # See COPYING for details.
6
+
7
+ """Spec-cleaner formatting module."""
8
+
9
+ import argparse
10
+ import os
11
+ import sys
12
+ from datetime import datetime
13
+ from typing import Any, Dict, List
14
+
15
+ from .rpmcleaner import RpmSpecCleaner
16
+ from .rpmexception import RpmExceptionError, RpmWrongArgsError
17
+
18
+ __version__ = '1.2.3'
19
+
20
+
21
+ def process_args(argv: List[str]) -> Dict[str, Any]:
22
+ """
23
+ Parse and process commandline arguments.
24
+
25
+ Args:
26
+ argv: A list of passed arguments.
27
+
28
+ Returns:
29
+ A dict mapping arguments to the corresponding values.
30
+
31
+ Raises:
32
+ RpmWrongArgsError: If the specfile doesn't exist or
33
+ if the output file already exists but '--force' option (overwrite the output) wasn't used.
34
+ """
35
+ parser = argparse.ArgumentParser(
36
+ prog='spec-cleaner',
37
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
38
+ description='Cleans the given spec file according to style guide and returns the result. If "#nospeccleaner"'
39
+ 'tag is used in the spec file than the spec file will not be cleaned.',
40
+ )
41
+
42
+ # Make the -d, -i, and -o exclusive as we can do only one of those
43
+ output_group = parser.add_mutually_exclusive_group()
44
+
45
+ parser.add_argument('specfile', metavar='SPEC', type=str, help='spec file to beautify')
46
+ parser.add_argument(
47
+ '-c',
48
+ '--cmake',
49
+ action='store_true',
50
+ help='convert dependencies to their cmake() counterparts, requires bit more of cleanup in spec afterwards.',
51
+ )
52
+ output_group.add_argument(
53
+ '-d',
54
+ '--diff',
55
+ action='store_true',
56
+ help='run the diff program to show differences between new and original specfile.',
57
+ )
58
+ parser.add_argument(
59
+ '--diff-prog', default='vimdiff', help='specify the diff binary to call with diff option.',
60
+ )
61
+ parser.add_argument(
62
+ '-f', '--force', action='store_true', help='overwrite the output file if already exist.',
63
+ )
64
+ output_group.add_argument(
65
+ '-i',
66
+ '--inline',
67
+ action='store_true',
68
+ help='inline the changes directly to the parsed file.',
69
+ )
70
+ parser.add_argument(
71
+ '-m',
72
+ '--minimal',
73
+ action='store_true',
74
+ help='run in minimal mode that does not do anything intrusive (ie. just sets the Copyright)',
75
+ )
76
+ parser.add_argument(
77
+ '--no-curlification',
78
+ action='store_true',
79
+ help='do not convert variables bracketing (%%{macro}) and keep it as it was on the input',
80
+ )
81
+ parser.add_argument(
82
+ '--no-copyright',
83
+ action='store_true',
84
+ help='do not include official SUSE copyright hear and just keep what is present',
85
+ )
86
+ parser.add_argument(
87
+ '--remove-groups', action='store_true', help='remove groups from the specfile.'
88
+ )
89
+ parser.add_argument(
90
+ '--copyright-year',
91
+ metavar='YYYY',
92
+ type=int,
93
+ default=datetime.now().year,
94
+ help='year to insert into the copyright header when re-generating it',
95
+ )
96
+ output_group.add_argument(
97
+ '-o', '--output', default='', help='specify the output file for the cleaned spec content.',
98
+ )
99
+ parser.add_argument(
100
+ '-p',
101
+ '--pkgconfig',
102
+ action='store_true',
103
+ help='convert dependencies to their pkgconfig() counterparts, requires bit more of cleanup in spec afterwards.',
104
+ )
105
+ parser.add_argument(
106
+ '--perl',
107
+ action='store_true',
108
+ help='convert dependencies to their perl() counterparts, highly expansive use carefully',
109
+ )
110
+ parser.add_argument(
111
+ '-t',
112
+ '--tex',
113
+ action='store_true',
114
+ help='convert dependencies to their tex() counterparts, requires verification of the output afterwards.',
115
+ )
116
+ parser.add_argument(
117
+ '-v',
118
+ '--version',
119
+ action='version',
120
+ version=__version__,
121
+ help='show package version and exit',
122
+ )
123
+ parser.add_argument(
124
+ '-k', '--keep-space', action='store_true', help='keep empty lines in preamble intact.',
125
+ )
126
+
127
+ # print help if there is no argument
128
+ if len(argv) < 1:
129
+ parser.print_help()
130
+ sys.exit(0)
131
+
132
+ options = parser.parse_args(args=argv)
133
+
134
+ # the spec must exist for us to do anything
135
+ if not os.path.exists(options.specfile):
136
+ raise RpmWrongArgsError('{0} does not exist.'.format(options.specfile))
137
+
138
+ # the path for output must exist and the file must not be there unless
139
+ # force is specified
140
+ if options.output:
141
+ options.output = os.path.expanduser(options.output)
142
+ if not options.force and os.path.exists(options.output):
143
+ raise RpmWrongArgsError('{0} already exists.'.format(options.output))
144
+
145
+ # convert options to dict
146
+ options_dict = vars(options)
147
+ return options_dict
148
+
149
+
150
+ def main() -> int:
151
+ """
152
+ Run main function.
153
+
154
+ It calls argument parsing ensures their sanity and then creates
155
+ RpmSpecCleaner object that works with passed spec file.
156
+ """
157
+ try:
158
+ options = process_args(sys.argv[1:])
159
+ except RpmWrongArgsError as exception:
160
+ sys.stderr.write('ERROR: {0}\n'.format(exception))
161
+ return 1
162
+
163
+ try:
164
+ cleaner = RpmSpecCleaner(options)
165
+ cleaner.run()
166
+ except RpmExceptionError as exception:
167
+ sys.stderr.write('ERROR: {0}\n'.format(exception))
168
+ return 1
169
+
170
+ return 0
@@ -0,0 +1,11 @@
1
+ import os
2
+ import sys
3
+
4
+ from spec_cleaner import main
5
+
6
+ path = os.path.dirname(os.path.dirname(__file__))
7
+ sys.path.insert(0, path)
8
+
9
+
10
+ if __name__ == '__main__':
11
+ sys.exit(main())
@@ -0,0 +1,273 @@
1
+ import logging
2
+ import re
3
+
4
+ from .rpmexception import NoMatchExceptionError
5
+ from .rpmrequirestoken import RpmRequiresToken
6
+
7
+ chunk_types = ('text', 'space', 'macro', 'operator', 'version')
8
+
9
+ state_types = ('start', 'name', 'operator', 'version')
10
+
11
+ re_brackets = {}
12
+ re_brackets['('] = re.compile(
13
+ r'(' + r'\(' + r'|' + r'\)' + r'|' + r'\\(' + r'|' + r'\\)' + r'|' + r'[^\()]+' + r')'
14
+ )
15
+
16
+ re_brackets['{'] = re.compile(
17
+ r'(' + r'\{' + r'|' + r'\}' + r'|' + r'\\{' + r'|' + r'\\}' + r'|' + r'[^\{}]+' + r')'
18
+ )
19
+
20
+ re_name = re.compile(r'[-A-Za-z0-9_~(){}@:;.+/*\[\]]+')
21
+ re_version = re.compile(r'[-A-Za-z0-9_~():.+]+')
22
+ re_spaces = re.compile(r'(\s*,\s*|\s+)')
23
+ re_macro_unbraced = re.compile('%[A-Za-z0-9_]{3,}')
24
+ re_version_operator = re.compile('(>=|<=|=>|=<|>|<|=)')
25
+
26
+ logger = logging.getLogger('DepParser')
27
+ # Switch to logging.DEBUG if needed
28
+ logger.setLevel(logging.ERROR)
29
+
30
+
31
+ def find_end_of_bracketed_macro(string, regex, opening, closing):
32
+ """Find the end of bracketed macros."""
33
+ # ommit the initial bracket, or %bracket
34
+ if string.startswith('%'):
35
+ macro = string[0:2]
36
+ string = string[2:]
37
+ else:
38
+ macro = string[0:1]
39
+ string = string[1:]
40
+ opened = 1
41
+ while opened and string:
42
+ try:
43
+ bite, string = consume_chars(regex, string)
44
+ except NoMatchExceptionError:
45
+ raise Exception('unexpected parser error when looking for end of macro')
46
+
47
+ if bite == opening:
48
+ opened += 1
49
+ elif bite == closing:
50
+ opened -= 1
51
+ macro += bite
52
+
53
+ if opened:
54
+ raise Exception('Unexpectedly met end of string when looking for end of macro')
55
+ return macro, string
56
+
57
+
58
+ def consume_chars(regex, string):
59
+ """Consume regex matches of all characters of a string."""
60
+ match = regex.match(string)
61
+ if match:
62
+ end = match.end()
63
+ return string[0:end], string[end:]
64
+ else:
65
+ raise NoMatchExceptionError(
66
+ 'Expected match failed (string: "%s", regex: "%s" )' % (string, regex.pattern)
67
+ )
68
+
69
+
70
+ def read_boolean(string):
71
+ """Read boolean macros."""
72
+ return find_end_of_bracketed_macro(string, re_brackets['('], '(', ')')
73
+
74
+
75
+ def matching_bracket(bracket):
76
+ """Find the appropriate matching brackets."""
77
+ if bracket == '{':
78
+ return '}'
79
+ elif bracket == '(':
80
+ return ')'
81
+ raise Exception(
82
+ 'Undefined bracket matching - add defintion of "%s" to ' 'matching_bracket()' % bracket
83
+ )
84
+
85
+
86
+ def read_macro(string):
87
+ """Read macro in brackets."""
88
+ opening = string[1]
89
+ closing = matching_bracket(opening)
90
+ return find_end_of_bracketed_macro(string, re_brackets[opening], opening, closing)
91
+
92
+
93
+ def read_next_chunk(string):
94
+ """Read string chunks after operators."""
95
+ chunk = ''
96
+ chunk_type = ''
97
+
98
+ if not string:
99
+ return '', '', 'text'
100
+
101
+ if string[0:2] in ('>=', '<=', '=>', '=<'):
102
+ chunk = string[0:2]
103
+ chunk_type = 'operator'
104
+ rest = string[2:]
105
+
106
+ elif string[0:1] in ('<', '>', '='):
107
+ chunk = string[0:1]
108
+ chunk_type = 'operator'
109
+ rest = string[1:]
110
+
111
+ elif string[0].isspace() or string[0] == ',':
112
+ chunk = ''
113
+ chunk_type = 'space'
114
+ rest = consume_chars(re_spaces, string)[1]
115
+
116
+ elif string[0:2] == '%%':
117
+ chunk = '%%'
118
+ chunk_type = 'text'
119
+ rest = string[2:]
120
+
121
+ elif string[0:2] in ('%{', '%('):
122
+ chunk, rest = read_macro(string)
123
+ chunk_type = 'macro'
124
+
125
+ elif string[0] == '%':
126
+ chunk, rest = consume_chars(re_macro_unbraced, string)
127
+ chunk_type = 'macro'
128
+
129
+ elif string[0] == '(':
130
+ chunk, rest = read_boolean(string)
131
+ chunk_type = 'macro'
132
+
133
+ else:
134
+ chunk, rest = consume_chars(re_name, string)
135
+ chunk_type = 'text'
136
+
137
+ return (rest, chunk, chunk_type)
138
+
139
+
140
+ class DepParserError(Exception):
141
+ """Exception definition for dependency parsing."""
142
+
143
+ pass
144
+
145
+
146
+ class DependencyParser:
147
+ """Dependency parsing class."""
148
+
149
+ def __init__(self, line):
150
+ """Initialize class."""
151
+ # adding comma will cause flush in the end of line
152
+ self.string = line + ', '
153
+ self.parsed = []
154
+ self.token = []
155
+ self.state = 'start'
156
+ self.space = False
157
+ self.token_name = ''
158
+ self.token_operator = None
159
+ self.token_version = None
160
+ self.go_on = True
161
+ while self.go_on:
162
+ self.string, self.next, self.next_type = read_next_chunk(self.string)
163
+ logger.debug(
164
+ """========
165
+ chunk: '%s'
166
+ chunk_type: '%s'
167
+ rest: '%s'
168
+ token: '%s'
169
+ parsed: '%s'""",
170
+ self.next,
171
+ self.next_type,
172
+ self.string,
173
+ self.token,
174
+ self.parsed,
175
+ )
176
+ self.state_change()
177
+
178
+ def flat_out(self):
179
+ """Flatten versioned definition to a single string."""
180
+ result = []
181
+ for name, operator, ver in self.parsed:
182
+ result.append(RpmRequiresToken(name, operator, ver))
183
+ return result
184
+
185
+ def flush(self):
186
+ """Clean token variables of the class."""
187
+ self.parsed.append((self.token_name, self.token_operator, self.token_version))
188
+ # cleanup state
189
+ self.token = []
190
+ self.token_name = ''
191
+ self.token_operator = None
192
+ self.token_version = None
193
+ if not self.string:
194
+ self.go_on = False
195
+
196
+ def reconstitute_token(self):
197
+ """Erase token."""
198
+ r = ''.join(self.token)
199
+ logger.debug("reconstituting '%s'", r)
200
+ self.token = []
201
+ return r
202
+
203
+ def name_state_change(self):
204
+ """Check name of next token."""
205
+ if self.next_type in ['text', 'macro']:
206
+ if self.space:
207
+ logger.debug('text after space --> flush')
208
+ self.token_name = self.reconstitute_token()
209
+ self.space = False
210
+ self.flush()
211
+ elif self.next_type == 'space':
212
+ self.space = True
213
+ elif self.next_type == 'operator':
214
+ self.token_name = self.reconstitute_token()
215
+ self.state = 'operator'
216
+ self.space = False
217
+
218
+ self.token.append(self.next)
219
+
220
+ def operator_state_change(self):
221
+ """Check next operator token."""
222
+ if self.next_type in ['text', 'macro']:
223
+ self.state = 'version'
224
+ self.token_operator = self.reconstitute_token()
225
+ self.space = False
226
+ elif self.next_type == 'space':
227
+ self.space = True
228
+ elif self.next_type == 'operator':
229
+ if self.space:
230
+ raise DepParserError('found operator after operator')
231
+
232
+ self.token.append(self.next)
233
+
234
+ def version_state_change(self):
235
+ """Check valid token after version."""
236
+ if self.next_type == 'text':
237
+ pass
238
+ elif self.next_type == 'space':
239
+ self.token_version = self.reconstitute_token()
240
+ self.flush()
241
+ self.state = 'name'
242
+ self.space = False
243
+ elif self.next_type == 'macro':
244
+ pass
245
+ elif self.next_type == 'operator':
246
+ raise DepParserError('found operator after version')
247
+
248
+ self.token.append(self.next)
249
+
250
+ def start_state_change(self):
251
+ """Check next token valid for starting definition."""
252
+ if self.next_type == 'text':
253
+ self.state = 'name'
254
+ elif self.next_type == 'space':
255
+ pass
256
+ elif self.next_type == 'macro':
257
+ self.state = 'name'
258
+ elif self.next_type == 'operator':
259
+ raise DepParserError('found operator when name expected')
260
+
261
+ self.token.append(self.next)
262
+
263
+ def state_change(self):
264
+ """Select which state change to check."""
265
+ if self.state == 'name':
266
+ self.name_state_change()
267
+ elif self.state == 'operator':
268
+ self.operator_state_change()
269
+ elif self.state == 'version':
270
+ self.version_state_change()
271
+ elif self.state == 'start':
272
+ self.start_state_change()
273
+ logger.debug('new state: %s', self.state)
@@ -0,0 +1,64 @@
1
+ # vim: set ts=4 sw=4 et: coding=UTF-8
2
+
3
+ import os
4
+ import sys
5
+ import sysconfig
6
+ from io import StringIO
7
+ from typing import IO
8
+
9
+ from .rpmexception import RpmExceptionError
10
+
11
+
12
+ def open_datafile(name: str) -> IO[str]:
13
+ """
14
+ Open data files.
15
+
16
+ Used all around so kept glob here for importing.
17
+
18
+ Args:
19
+ name: A string representing the name of the datafile to open.
20
+
21
+ Raises:
22
+ RpmExceptionError if the file is not found in predefined datadirs.
23
+ """
24
+ homedir = os.getenv('HOME', '~') + '/.local/'
25
+
26
+ possible_paths = (
27
+ '{0}/../data/{1}'.format(os.path.dirname(os.path.realpath(__file__)), name),
28
+ '{0}/share/spec-cleaner/{1}'.format(homedir, name),
29
+ '{0}/share/spec-cleaner/{1}'.format(sysconfig.get_path('data'), name),
30
+ '{0}/share/spec-cleaner/{1}'.format(sys.prefix, name),
31
+ )
32
+
33
+ for path in possible_paths:
34
+ try:
35
+ _file = open(path, mode='r')
36
+ except OSError:
37
+ pass
38
+ else:
39
+ return _file
40
+ # file not found
41
+ raise RpmExceptionError("File '{}' not found in datadirs".format(name))
42
+
43
+
44
+ def open_stringio_spec(name: str) -> IO[str]:
45
+ """
46
+ Open regular files with exception handling.
47
+
48
+ Args:
49
+ name: A string with the file name.
50
+
51
+ Returns:
52
+ A file object.
53
+
54
+ Raises:
55
+ RpmExceptionError if the file is not readable.
56
+ """
57
+ data = StringIO()
58
+ try:
59
+ with open(name, mode='r') as f:
60
+ data.write(f.read())
61
+ data.seek(0, 0)
62
+ except (IOError, UnicodeDecodeError) as error:
63
+ raise RpmExceptionError(str(error))
64
+ return data
@@ -0,0 +1,56 @@
1
+ # vim: set ts=4 sw=4 et: coding=UTF-8
2
+
3
+ # We basically extend rpmcheck
4
+ from .rpmcheck import RpmCheck
5
+
6
+
7
+ class RpmBuild(RpmCheck):
8
+ """A class providing methods for %build section cleaning."""
9
+
10
+ def add(self, line: str) -> None:
11
+ # we do not want to run suseupdateconfig, deprecated
12
+ if self.reg.re_suseupdateconfig.search(line):
13
+ return
14
+
15
+ # if user uses cmake/configure directly just recommend him using the macros
16
+ if not self.minimal:
17
+ self._comment_macro_calls(line)
18
+
19
+ RpmCheck.add(self, line)
20
+
21
+ def _comment_macro_calls(self, line: str) -> None:
22
+ """
23
+ Add a comment if the package uses direct call of build tools instead of macro.
24
+
25
+ Args:
26
+ line: A string representing a line to process.
27
+ """
28
+ # if the line itself is comment then we do not add any fixme on top of them
29
+ if self.reg.re_comment.match(line):
30
+ return
31
+ # otherwise process the fixes
32
+ if self.reg.re_configure.match(line):
33
+ msg = '# FIXME: you should use the %%configure macro'
34
+ elif self.reg.re_cmake.match(line):
35
+ msg = '# FIXME: you should use the %%cmake macros'
36
+ elif self.reg.re_meson.match(line):
37
+ msg = '# FIXME: you should use the %%meson macros'
38
+ elif self.reg.re_qmake5.match(line):
39
+ msg = '# FIXME: you should use the %%qmake5 macro'
40
+ else:
41
+ return
42
+
43
+ position = len(self.lines)
44
+ if self.previous_line and self.previous_line.endswith('\\'):
45
+ # Backtrack until we are on top of multiline command
46
+ for i in reversed(self.lines):
47
+ if i.endswith('\\'):
48
+ position -= 1
49
+ else:
50
+ # we stop backtracking on the first
51
+ # non-backslashed line
52
+ break
53
+ # Check if on the position above is no comment
54
+ # if not add our message
55
+ if not self.lines[position - 1].startswith('#'):
56
+ self.lines.insert(position, msg)
@@ -0,0 +1,77 @@
1
+ # vim: set ts=4 sw=4 et: coding=UTF-8
2
+
3
+ from .rpmsection import Section
4
+
5
+
6
+ class RpmCheck(Section):
7
+ """
8
+ A class providing methods for %check section cleaning.
9
+
10
+ Replace various troublemakers in check phase.
11
+ """
12
+
13
+ def add(self, line: str) -> None:
14
+ line = self._complete_cleanup(line)
15
+
16
+ # smp_mflags for jobs macro replacement
17
+ line = self.reg.re_jobs.sub('%{?_smp_mflags}', line)
18
+
19
+ if not self.minimal:
20
+ line = self._replace_pytest(line)
21
+ line = self._replace_unittest(line)
22
+ line = self._replace_make(line)
23
+
24
+ Section.add(self, line)
25
+
26
+ def _replace_pytest(self, line: str) -> str:
27
+ """
28
+ Replace various pytest calls with %pytest or %pytest_arch macros.
29
+
30
+ Args:
31
+ line: A string representing a line to process.
32
+
33
+ Return:
34
+ The processed line.
35
+ """
36
+ line = self.reg.re_pytest.sub('%pytest', line)
37
+ line = self.reg.re_pytest_arch.sub('%pytest_arch', line)
38
+ return line
39
+
40
+ def _replace_unittest(self, line: str) -> str:
41
+ """
42
+ Replace various pytest calls with %pyunittest or %pyunittest_arch macros.
43
+
44
+ Args:
45
+ line: A string representing a line to process.
46
+
47
+ Return:
48
+ The processed line.
49
+ """
50
+ line = self.reg.re_pyunittest.sub('%pyunittest', line)
51
+ line = self.reg.re_pyunittest_arch.sub('%pyunittest_arch', line)
52
+ return line
53
+
54
+ def _replace_make(self, line: str) -> str:
55
+ """
56
+ Replace 'make' call with '%make_build' macro.
57
+
58
+ Args:
59
+ line: A string representing a line to process.
60
+
61
+ Return:
62
+ The processed line.
63
+ """
64
+ # if it is comment line then do nothing
65
+ if line.lstrip().startswith('#'):
66
+ return line
67
+ # find all make calls and replace them with %make_build
68
+ if self.reg.re_make.match(line):
69
+ line = self.reg.re_make.sub(r'\1%make_build\2', line)
70
+ # remove from line all the options that are part of %make_build macro
71
+ if self.reg.re_make_build.match(line):
72
+ # remove smp flags
73
+ line = line.replace(' %{?_smp_mflags}', '')
74
+ # remove verbosity
75
+ line = line.replace(' V=1', '')
76
+ line = line.replace(' VERBOSE=1', '')
77
+ return line