|
1 | | -#!/usr/bin/perl |
2 | | -# |
3 | | -# A script to clean up spec files and bring them closer to ROSA packaging policies |
4 | | -# |
5 | | -# Author: Denis Silakov, ROSA, denis.silakov@rosalab.com |
6 | | -# |
7 | | - |
8 | | -$spec = $ARGV[0]; |
9 | | -$newspec = $ARGV[1]; |
10 | | - |
11 | | -use Env qw(SPEC_LEAVE_CHANGELOG); |
12 | | - |
13 | | -$pkgname = $spec; |
14 | | -$pkgname =~ s/\.spec$//; |
15 | | - |
16 | | -if (!$newspec) { |
17 | | - $newspec = $spec.".tmp"; |
18 | | -} |
19 | | - |
20 | | -if (@ARGV < 1) { |
21 | | - die "Usage: $0 OLD_SPEC [NEW_SPEC]"; |
22 | | -} |
23 | | - |
24 | | -$changelog = 0; |
25 | | - |
26 | | -# List of macros known to be variables, so they should be embraced with '{}' |
27 | | -my @vars = ( |
28 | | -'name', |
29 | | -'version', |
30 | | -'release', |
31 | | -'eposh', |
32 | | -'EVRD', |
33 | | -'py_ver', |
34 | | -'buildroot', |
35 | | -'optflags', |
36 | | -'ldflags', |
37 | | -'build_ldflags', |
38 | | -'_bindir', |
39 | | -'_libdir', |
40 | | -'rlibdir', |
41 | | -'_sbindir', |
42 | | -'_mandir', |
43 | | -'_datadir', |
44 | | -'_sysconfdir', |
45 | | -'_webappconfdir', |
46 | | -'_webconfdir', |
47 | | -'_jvmcommonsysconfdir', |
48 | | -'_jvmsysconfdir', |
49 | | -'_mavendepmapfragdir', |
50 | | -'_mavendepmapdir', |
51 | | -'_initddir', |
52 | | -'_sys_macros_dir', |
53 | | -'_systemdconfdir', |
54 | | -'_systemdrootdir', |
55 | | -'_systemgeneratordir', |
56 | | -'_systemshutdowndir', |
57 | | -'_systemunitdir', |
58 | | -'_unitdir', |
59 | | -'_userunitdir', |
60 | | -'_sharedstatedir', |
61 | | -'_desktopdir', |
62 | | -'_gamesbindir', |
63 | | -'_includedir', |
64 | | -'firefox_pluginsdir', |
65 | | -'_jnidir', |
66 | | -'_jvmcommonlibdir', |
67 | | -'_jvmjardir', |
68 | | -'_jvmdir', |
69 | | -'_jvmprivdir', |
70 | | -'_kde_plugindir', |
71 | | -'_menudir', |
72 | | -'_qt_bindir', |
73 | | -'_qt_demodir', |
74 | | -'_qt_exampledir', |
75 | | -'_qt_importdir', |
76 | | -'qt4include', |
77 | | -'_qt_includedir', |
78 | | -'qt4plugins', |
79 | | -'_qt_plugindir', |
80 | | -'qt4dir', |
81 | | -'_qt_translationdir', |
82 | | -'_systemdlibexecdir', |
83 | | -'tcl_sitearch', |
84 | | -'_usergeneratordir', |
85 | | -'sampler_libdir', |
86 | | -'_kde_applicationsdir', |
87 | | -'_kde_appsdir', |
88 | | -'_kde_configdir', |
89 | | -'_qt_docdir', |
90 | | -'_lispdir', |
91 | | -'_xfontdir', |
92 | | -'_gamesdatadir', |
93 | | -'_icons16dir', |
94 | | -'_icons192dir', |
95 | | -'_icons22dir', |
96 | | -'_icons48dir', |
97 | | -'_icons64dir', |
98 | | -'_icons96dir', |
99 | | -'_iconsbasedir', |
100 | | -'_iconsscaldir', |
101 | | -'_iconsdir', |
102 | | -'_liconsdir', |
103 | | -'_miconsdir', |
104 | | -'_infodir', |
105 | | -'_javadocdir', |
106 | | -'_javadir', |
107 | | -'_jvmcommondatadir', |
108 | | -'_jvmdatadir', |
109 | | -'_localedir', |
110 | | -'_mavenpomdir', |
111 | | -'php_pear_dir', |
112 | | -'_rpm_helper_dir', |
113 | | -'_spec_helper_dir', |
114 | | -'_systemddatadir', |
115 | | -'_userunitdir', |
116 | | -'_prefix', |
117 | | -'tcl_sitelib', |
118 | | -'_filetriggers_dir', |
119 | | -'_localstatedir', |
120 | | -'_logdir', |
121 | | -'_repackage_dir', |
122 | | -'ruby_gemdir', |
123 | | -'ruby_ridir', |
124 | | -'ruby_libdir', |
125 | | -'ruby_sitelibdir', |
126 | | -'ruby_vendorlibdir', |
127 | | -'ruby_sitedir', |
128 | | -'ruby_vendordir', |
129 | | -'py3_platsitedir', |
130 | | -'py3_platlibdir', |
131 | | -'py3_incdir', |
132 | | -'py_incdir', |
133 | | -'py_platsitedir', |
134 | | -'py_platlibdir', |
135 | | -'py_dyndir', |
136 | | -'py_puresitedir', |
137 | | -'py_purelibdir', |
138 | | -'py3_puresitedir', |
139 | | -'py3_purelibdir', |
140 | | -'perl_archlib', |
141 | | -'perl_privlib', |
142 | | -'perl_sitearch', |
143 | | -'perl_sitelib', |
144 | | -'perl_vendorarch', |
145 | | -'perl_vendorlib', |
146 | | -'perl_version', |
147 | | - |
148 | | -); |
149 | | - |
150 | | -my @macros = ( |
151 | | -'make', |
152 | | -'make_build', |
153 | | -'make_install', |
154 | | -'makeinstall_std', |
155 | | -'configure', |
156 | | -'configure2_5x', |
157 | | - |
158 | | -); |
159 | | - |
160 | | -my @macros_to_unroll = ( |
161 | | -'__mkdir_p', |
162 | | -'__mkdir', |
163 | | -'__ln_s', |
164 | | -'__ln', |
165 | | -'__mv', |
166 | | -'__perl', |
167 | | -'__python', |
168 | | -'__python3', |
169 | | -'__rm', |
170 | | -'__rmdir', |
171 | | -'__ruby', |
172 | | -'__cp', |
173 | | -'__grep', |
174 | | -'__cat', |
175 | | -'__chmod', |
176 | | -'__chown', |
177 | | -'__install', |
178 | | - |
179 | | -); |
180 | | - |
181 | | -# Indicates that we are inside post- or pre- script |
182 | | -$in_script=0; |
183 | | - |
184 | | -open(F, $spec); |
185 | | -open(G, "> $newspec"); |
186 | | -while(<F>) { |
187 | | - if( /^BuildRoot:/i or /^Packager:/i ) { |
188 | | - next; |
189 | | - } |
190 | | - |
191 | | - # Obsolete |
192 | | - if( /^(Build)?Requires(\(.*\))?:\s*info-install\s*$/i ) { |
193 | | - next; |
194 | | - } |
195 | | - |
196 | | - $line = $_; |
197 | | - |
198 | | - # Captitalize summary and drop dot from its end, if any |
199 | | - if( $line =~ /^(%define\s*)?Summary:\s*(\S+)/i ) { |
200 | | - $word = $2; |
201 | | - $word_old = $word; |
202 | | - $word =~ s/(^[a-z])/\u$1/; |
203 | | - if( $word_old !~ /^$pkgname/ ) { |
204 | | - $line =~ s/Summary:(\s*)$word_old/Summary:$1$word/; |
205 | | - } else { |
206 | | - print STDERR "Won't capitalize summary - it starts with project name\n"; |
207 | | - } |
208 | | - |
209 | | - chomp $line; |
210 | | - if( $line =~ /\.\s*$/ ) { |
211 | | - $line =~ s/\.\s*$//; |
212 | | - } |
213 | | - print G $line."\n"; |
214 | | - next; |
215 | | - } |
216 | | - |
217 | | - if( /^%changelog/i ) { |
218 | | - $changelog = 1; |
219 | | - # By default, we drop changelogs from specs |
220 | | - # nowadays changelogs are generated from Git |
221 | | - # and most spec files contains only ancient changelog entries |
222 | | - if( !$SPEC_LEAVE_CHANGELOG ) { |
223 | | - last; |
224 | | - } |
225 | | - } |
226 | | - |
227 | | - if( $changelog or /\s*#/) { |
228 | | - print G $line; |
229 | | - next; |
230 | | - } |
231 | | - |
232 | | - if( $line =~ /^\%defattr\(-,root,root(,-)?\)\s*$/ ) { |
233 | | - next; |
234 | | - } |
235 | | - |
236 | | - $line =~ s/\$\{?RPM_BUILD_ROOT\}?/\%\{buildroot\}/g; |
237 | | - $line =~ s/\$\{?RPM_OPT_FLAGS\}?/\%\{optflags\}/g; |
238 | | - |
239 | | - if( $line =~/^\s*(\%\{?__rm\}?|rm) -rf \%\{?buildroot\}?\s*$/ ) { |
240 | | - next; |
241 | | - } |
242 | | - |
243 | | - $line =~ s/\%mkrel\s+//; |
244 | | - $line =~ s/\%\{\?dist\}//; |
245 | | - $line =~ s/make \%\{\?_smp_mflags\}/\%make_build/; |
246 | | - $line =~ s/\%make test/\make test/; |
247 | | - |
248 | | - # Cleanup old grep usage |
249 | | - $line =~ s/fgrep/grep -F/g; |
250 | | - $line =~ s/egrep/grep -E/g; |
251 | | - |
252 | | - # Clean up obsolete macro usage |
253 | | - $line =~ s/({|%)webappconfdir/$1_webappconfdir/g; |
254 | | - $line =~ s/({|%)py_libdir/$1py_purelibdir/g; |
255 | | - $line =~ s/({|%)py_sitedir/$1py_puresitedir/g; |
256 | | - $line =~ s/({|%)python_sitearch/$1py_platsitedir/g; |
257 | | - $line =~ s/({|%)python_sitelib/$1py_puresitedir/g; |
258 | | - $line =~ s/({|%)python3_sitearch/$1py3_platsitedir/g; |
259 | | - $line =~ s/({|%)python3_sitelib/$1py3_puresitedir/g; |
260 | | - $line =~ s/({|%)python_version/$1py_ver/g; |
261 | | - $line =~ s/({|%)pyver/$1py_ver/g; |
262 | | - $line =~ s/\%py_requires -d/BuildRequires: pkgconfig\(python\)/; |
263 | | - |
264 | | - foreach $var (@vars) { |
265 | | - $line =~ s/\%$var/\%\{$var\}/g; |
266 | | - } |
267 | | - |
268 | | - foreach $macro (@macros) { |
269 | | - $line =~ s/\%\{$macro\}/\%$macro/g; |
270 | | - $line =~ s/\%\{__$macro\}/\%$macro/g; |
271 | | - } |
272 | | - |
273 | | - foreach $macro (@macros_to_unroll) { |
274 | | - my $repl = $macro; |
275 | | - $repl =~ s/^__//; |
276 | | - $repl =~ s/_/ -/; |
277 | | - $line =~ s/\%\{?$macro\}?/$repl/g; |
278 | | - } |
279 | | - |
280 | | - foreach my $cmd ('mv', 'cp', 'python', 'rm') { |
281 | | - $line =~ s/\%\{_?_?$cmd\}/$cmd/g; |
282 | | - } |
283 | | - |
284 | | - # Drop definitions of %name, %version and %release |
285 | | - if( /\%define\s+name\s+(.+)$/ ) { |
286 | | - $name = $1; |
287 | | - next; |
288 | | - } |
289 | | - |
290 | | - if( /\%define\s+version\s+(.+)$/ ) { |
291 | | - $version = $1; |
292 | | - next; |
293 | | - } |
294 | | - |
295 | | - if( /\%define\s+release\s+(.+)$/ ) { |
296 | | - $release = $1; |
297 | | - next; |
298 | | - } |
299 | | - |
300 | | - # If Name, Version or Release are defined using %name, %version and %relese macros |
301 | | - # replace macros with their values |
302 | | - # (manual definition of these macros is obsolete and not needed, they are get defined |
303 | | - # automatically on the basis of Name, Version and Release tags) |
304 | | - if( /^Name:(\s+)\%\{?name\}?$/ ) { |
305 | | - print G "Name:$1$name\n"; |
306 | | - next; |
307 | | - } |
308 | | - |
309 | | - if( /^Version:(\s+)\%\{?version\}?$/ ) { |
310 | | - print G "Version:$1$version\n"; |
311 | | - next; |
312 | | - } |
313 | | - |
314 | | - if( /^Release:(\s+)\%\{?release\}?$/ ) { |
315 | | - print G "Release:$1$release\n"; |
316 | | - next; |
317 | | - } |
318 | | - |
319 | | - print G $line; |
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +import re |
| 4 | +import sys |
| 5 | + |
| 6 | +# Dictionary of macros to replace: key is the macro, value is its replacement |
| 7 | +MACROS_TO_REPLACE = { |
| 8 | + r'%make\b': '%make_build', |
| 9 | + r'%makeinstall_std\b': '%make_install', |
| 10 | + r'%configure2_5x\b': '%configure', |
| 11 | + r'%apply_patches\b': '%autopatch -p1', |
| 12 | + r'\$\{?RPM_BUILD_ROOT\}?': '%{buildroot}', |
| 13 | + # Add more macros here as needed |
| 14 | + # Example: |
| 15 | + # r'%old_macro\b': '%new_macro', |
320 | 16 | } |
321 | 17 |
|
322 | | -# newspec was not specified in command line, |
323 | | -# so we are using temoporary file; now let's move it |
324 | | -# to original one |
325 | | -if (!$ARGV[1]) { |
326 | | - system "cat $newspec > $spec"; |
327 | | - system "rm -f $newspec"; |
328 | | -} |
| 18 | +def replace_macros_and_clean(file_path): |
| 19 | + try: |
| 20 | + # Read the file content |
| 21 | + with open(file_path, 'r') as file: |
| 22 | + original_content = file.read() |
| 23 | + content = original_content |
| 24 | + |
| 25 | + # Track changes |
| 26 | + changes = [] |
| 27 | + |
| 28 | + # Replace macros using the dictionary |
| 29 | + for old_macro, new_macro in MACROS_TO_REPLACE.items(): |
| 30 | + # Use regex to replace only whole macros |
| 31 | + new_content, count = re.subn(old_macro, new_macro, content) |
| 32 | + if count > 0: |
| 33 | + changes.append(f"Replaced '{old_macro}' with '{new_macro}' {count} times") |
| 34 | + content = new_content |
| 35 | + |
| 36 | + # Remove trailing spaces from each line |
| 37 | + lines = content.splitlines() |
| 38 | + cleaned_lines = [line.rstrip() for line in lines] |
| 39 | + cleaned_content = "\n".join(cleaned_lines) + "\n" # Ensure trailing newline |
| 40 | + |
| 41 | + # Check if trailing spaces were removed |
| 42 | + if cleaned_content != content: |
| 43 | + changes.append("Removed trailing spaces from all lines") |
| 44 | + |
| 45 | + # Check if any changes were made |
| 46 | + if changes: |
| 47 | + # Write the modified content back to the file |
| 48 | + with open(file_path, 'w') as file: |
| 49 | + file.write(cleaned_content) |
| 50 | + print(f"Changes in file '{file_path}':") |
| 51 | + for change in changes: |
| 52 | + print(f" - {change}") |
| 53 | + else: |
| 54 | + print(f"Nothing to clean in file '{file_path}'.") |
| 55 | + except FileNotFoundError: |
| 56 | + print(f"Error: File '{file_path}' not found.") |
| 57 | + except Exception as e: |
| 58 | + print(f"An error occurred: {e}") |
| 59 | + |
| 60 | +if __name__ == "__main__": |
| 61 | + if len(sys.argv) != 2: |
| 62 | + print("Usage: /usr/bin/spec-cleaner foo.spec") |
| 63 | + else: |
| 64 | + file_path = sys.argv[1] |
| 65 | + replace_macros_and_clean(file_path) |
0 commit comments