]>
Commit | Line | Data |
---|---|---|
1270549a CD |
1 | #!/usr/bin/python3 |
2 | # Sort Makefile lines as expected by project policy. | |
dff8da6b | 3 | # Copyright (C) 2023-2024 Free Software Foundation, Inc. |
1270549a CD |
4 | # This file is part of the GNU C Library. |
5 | # | |
6 | # The GNU C Library is free software; you can redistribute it and/or | |
7 | # modify it under the terms of the GNU Lesser General Public | |
8 | # License as published by the Free Software Foundation; either | |
9 | # version 2.1 of the License, or (at your option) any later version. | |
10 | # | |
11 | # The GNU C Library is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | # Lesser General Public License for more details. | |
15 | # | |
16 | # You should have received a copy of the GNU Lesser General Public | |
17 | # License along with the GNU C Library; if not, see | |
18 | # <https://www.gnu.org/licenses/>. | |
19 | ||
20 | # The project consensus is to split Makefile variable assignment | |
21 | # across multiple lines with one value per line. The values are | |
22 | # then sorted as described below, and terminated with a special | |
23 | # list termination marker. This splitting makes it much easier | |
24 | # to add new tests to the list since they become just a single | |
25 | # line insertion. It also makes backports and merges easier | |
26 | # since the new test may not conflict due to the ordering. | |
27 | # | |
28 | # Consensus discussion: | |
29 | # https://inbox.sourceware.org/libc-alpha/f6406204-84f5-adb1-d00e-979ebeebbbde@redhat.com/ | |
30 | # | |
31 | # To support cleaning up Makefiles we created this program to | |
32 | # help sort existing lists converted to the new format. | |
33 | # | |
34 | # The program takes as input the Makefile to sort correctly, | |
35 | # and the output file to write the correctly sorted output | |
36 | # (it can be the same file). | |
37 | # | |
38 | # Sorting is only carried out between two special markers: | |
39 | # (a) Marker start is '<variable> += \' (or '= \', or ':= \') | |
40 | # (b) Marker end is ' # <variable>' (whitespace matters) | |
2cbeda84 | 41 | # With everything between (a) and (b) being sorted accordingly. |
1270549a CD |
42 | # |
43 | # You can use it like this: | |
44 | # $ scripts/sort-makefile-lines.py < elf/Makefile > elf/Makefile.tmp | |
45 | # $ mv elf/Makefile.tmp elf/Makefile | |
46 | # | |
47 | # The Makefile lines in the project are sorted using the | |
48 | # following rules: | |
49 | # - All lines are sorted as-if `LC_COLLATE=C sort` | |
50 | # - Lines that have a numeric suffix and whose leading prefix | |
51 | # matches exactly are sorted according the numeric suffix | |
52 | # in increasing numerical order. | |
53 | # | |
54 | # For example: | |
55 | # ~~~ | |
56 | # tests += \ | |
57 | # test-a \ | |
58 | # test-b \ | |
59 | # test-b1 \ | |
60 | # test-b2 \ | |
61 | # test-b10 \ | |
62 | # test-b20 \ | |
63 | # test-b100 \ | |
64 | # # tests | |
65 | # ~~~ | |
66 | # This example shows tests sorted alphabetically, followed | |
67 | # by a numeric suffix sort in increasing numeric order. | |
68 | # | |
69 | # Cleanups: | |
70 | # - Tests that end in "a" or "b" variants should be renamed to | |
71 | # end in just the numerical value. For example 'tst-mutex7robust' | |
72 | # should be renamed to 'tst-mutex12' (the highest numbered test) | |
73 | # or 'tst-robust11' (the highest numbered test) in order to get | |
74 | # reasonable ordering. | |
75 | # - Modules that end in "mod" or "mod1" should be renamed. For | |
76 | # example 'tst-atfork2mod' should be renamed to 'tst-mod-atfork2' | |
77 | # (test module for atfork2). If there are more than one module | |
78 | # then they should be named with a suffix that uses [0-9] first | |
79 | # then [A-Z] next for a total of 36 possible modules per test. | |
80 | # No manually listed test currently uses more than that (though | |
81 | # automatically generated tests may; they don't need sorting). | |
82 | # - Avoid including another test and instead refactor into common | |
2cbeda84 | 83 | # code with all tests including the common code, then give the |
1270549a CD |
84 | # tests unique names. |
85 | # | |
86 | # If you have a Makefile that needs converting, then you can | |
87 | # quickly split the values into one-per-line, ensure the start | |
88 | # and end markers are in place, and then run the script to | |
89 | # sort the values. | |
90 | ||
91 | import sys | |
92 | import locale | |
93 | import re | |
94 | import functools | |
95 | ||
96 | def glibc_makefile_numeric(string1, string2): | |
97 | # Check if string1 has a numeric suffix. | |
98 | var1 = re.search(r'([0-9]+) \\$', string1) | |
99 | var2 = re.search(r'([0-9]+) \\$', string2) | |
100 | if var1 and var2: | |
101 | if string1[0:var1.span()[0]] == string2[0:var2.span()[0]]: | |
102 | # string1 and string2 both share a prefix and | |
103 | # have a numeric suffix that can be compared. | |
104 | # Sort order is based on the numeric suffix. | |
b0528456 CD |
105 | # If the suffix is the same return 0, otherwise |
106 | # > 0 for greater-than, and < 0 for less-than. | |
107 | # This is equivalent to the numerical difference. | |
108 | return int(var1.group(1)) - int(var2.group(1)) | |
1270549a CD |
109 | # Default to strcoll. |
110 | return locale.strcoll(string1, string2) | |
111 | ||
112 | def sort_lines(lines): | |
113 | ||
114 | # Use the C locale for language independent collation. | |
115 | locale.setlocale (locale.LC_ALL, "C") | |
116 | ||
117 | # Sort using a glibc-specific sorting function. | |
118 | lines = sorted(lines, key=functools.cmp_to_key(glibc_makefile_numeric)) | |
119 | ||
120 | return lines | |
121 | ||
122 | def sort_makefile_lines(): | |
123 | ||
124 | # Read the whole Makefile. | |
125 | lines = sys.stdin.readlines() | |
126 | ||
127 | # Build a list of all start markers (tuple includes name). | |
128 | startmarks = [] | |
129 | for i in range(len(lines)): | |
130 | # Look for things like "var = \", "var := \" or "var += \" | |
131 | # to start the sorted list. | |
6a2512bf | 132 | var = re.search(r'^([-_a-zA-Z0-9]*) [\+:]?\= \\$', lines[i]) |
1270549a CD |
133 | if var: |
134 | # Remember the index and the name. | |
135 | startmarks.append((i, var.group(1))) | |
136 | ||
137 | # For each start marker try to find a matching end mark | |
138 | # and build a block that needs sorting. The end marker | |
139 | # must have the matching comment name for it to be valid. | |
140 | rangemarks = [] | |
141 | for sm in startmarks: | |
142 | # Look for things like " # var" to end the sorted list. | |
6a2512bf | 143 | reg = r'^ *# ' + sm[1] + r'$' |
1270549a CD |
144 | for j in range(sm[0] + 1, len(lines)): |
145 | if re.search(reg, lines[j]): | |
2cbeda84 | 146 | # Remember the block to sort (inclusive). |
1270549a CD |
147 | rangemarks.append((sm[0] + 1, j)) |
148 | break | |
149 | ||
150 | # We now have a list of all ranges that need sorting. | |
151 | # Sort those ranges (inclusive). | |
152 | for r in rangemarks: | |
153 | lines[r[0]:r[1]] = sort_lines(lines[r[0]:r[1]]) | |
154 | ||
155 | # Output the whole list with sorted lines to stdout. | |
156 | [sys.stdout.write(line) for line in lines] | |
157 | ||
158 | ||
159 | def main(argv): | |
160 | sort_makefile_lines () | |
161 | ||
162 | if __name__ == '__main__': | |
163 | main(sys.argv[1:]) |