xref: /freebsd/share/mk/version_gen.awk (revision 94cba8034ba53725c225c85e35724f0c2b13cea5)
1#
2# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3#
4# Copyright (C) 2006 Daniel M. Eischen.  All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26#
27# $FreeBSD$
28#
29
30#
31# Make a list of all the library versions listed in the master file.
32#
33#   versions[] - array indexed by version name, contains number
34#                of symbols (+ 1) found for each version.
35#   successors[] - array index by version name, contains successor
36#                  version name.
37#   symbols[][] - array index by [version name, symbol index], contains
38#                 names of symbols defined for each version.
39#   names[] - array index is symbol name and value is its first version seen,
40#	      used to check for duplicate symbols and warn about them.
41#
42BEGIN {
43	brackets = 0;
44	errors = warns = 0;
45	version_count = 0;
46	current_version = "";
47	stderr = "/dev/stderr";
48	while (getline < vfile) {
49		# Strip comments.
50		sub("#.*$", "", $0);
51
52		# Strip leading and trailing whitespace.
53		sub("^[ \t]+", "", $0);
54		sub("[ \t]+$", "", $0);
55
56		if (/^[a-zA-Z0-9._]+[ \t]*{$/) {
57			# Strip brace.
58			sub("{", "", $1);
59			brackets++;
60			symver = $1;
61			versions[symver] = 1;
62			successors[symver] = "";
63			generated[symver] = 0;
64			version_count++;
65		}
66		else if (/^}[ \t]*[a-zA-Z0-9._]+[ \t]*;$/) {
67			v = $1 != "}" ? $1 : $2;
68			# Strip brace.
69			sub("}", "", v);
70			# Strip semicolon.
71			sub(";", "", v);
72			if (symver == "") {
73				printf("File %s: Unmatched bracket.\n",
74				vfile) > stderr;
75				errors++;
76			}
77			else if (versions[v] != 1) {
78				printf("File %s: `%s' has unknown " \
79				    "successor `%s'.\n",
80				    vfile, symver, v) > stderr;
81				errors++;
82			}
83			else
84				successors[symver] = v;
85			brackets--;
86		}
87		else if (/^}[ \t]*;$/) {
88			if (symver == "") {
89				printf("File %s: Unmatched bracket.\n",
90				    vfile) > stderr;
91				errors++;
92			}
93			# No successor
94			brackets--;
95		}
96		else if (/^}$/) {
97			printf("File %s: Missing final semicolon.\n",
98			    vfile) > stderr;
99			errors++;
100		}
101		else if (/^$/)
102			;  # Ignore blank lines.
103		else {
104			printf("File %s: Unknown directive: `%s'.\n",
105			    vfile, $0) > stderr;
106			errors++;
107		}
108	}
109	brackets = 0;
110}
111
112{
113	# Set meaningful filename for diagnostics.
114	filename = FILENAME != "" ? FILENAME : "<stdin>";
115
116	# Delete comments, preceding and trailing whitespace, then
117	# consume blank lines.
118	sub("#.*$", "", $0);
119	sub("^[ \t]+", "", $0);
120	sub("[ \t]+$", "", $0);
121	if ($0 == "")
122		next;
123}
124
125/^[a-zA-Z0-9._]+[ \t]*{$/ {
126	# Strip bracket from version name.
127	sub("{", "", $1);
128	if (current_version != "") {
129		printf("File %s, line %d: Illegal nesting detected.\n",
130		    filename, FNR) > stderr;
131		errors++;
132	}
133	else if (versions[$1] == 0) {
134		printf("File %s, line %d: Undefined " \
135		    "library version `%s'.\n", filename, FNR, $1) > stderr;
136		errors++;
137		# Remove this entry from the versions.
138		delete versions[$1];
139	}
140	else
141		current_version = $1;
142	brackets++;
143	next;
144}
145
146/^[a-zA-Z0-9._]+[ \t]*;$/ {
147	# Strip semicolon.
148	sub(";", "", $1);
149	if (current_version != "") {
150		count = versions[current_version];
151		versions[current_version]++;
152		symbols[current_version, count] = $1;
153		if ($1 in names && names[$1] != current_version) {
154			#
155			# A graver case when a dup symbol appears under
156			# different versions in the map.  That can result
157			# in subtle problems with the library later.
158			#
159			printf("File %s, line %d: Duplicated symbol `%s' " \
160			    "in version `%s', first seen in `%s'. " \
161			    "Did you forget to move it to ObsoleteVersions?\n",
162			    filename, FNR, $1,
163			    current_version, names[$1]) > stderr;
164			errors++;
165		}
166		else if (names[$1] == current_version) {
167			#
168			# A harmless case: a dup symbol with the same version.
169			#
170			printf("File %s, line %d: warning: " \
171			    "Duplicated symbol `%s' in version `%s'.\n",
172			    filename, FNR, $1, current_version) > stderr;
173			warns++;
174		}
175		else
176			names[$1] = current_version;
177	}
178	else {
179		printf("File %s, line %d: Symbol `%s' outside version scope.\n",
180		    filename, FNR, $1) > stderr;
181		errors++;
182	}
183	next;
184}
185
186/^}[ \t]*;$/ {
187	brackets--;
188	if (brackets < 0) {
189		printf("File %s, line %d: Unmatched bracket.\n",
190		    filename, FNR, $1) > stderr;
191		errors++;
192		brackets = 0;	# Reset
193	}
194	current_version = "";
195	next;
196}
197
198
199{
200	printf("File %s, line %d: Unknown directive: `%s'.\n",
201	    filename, FNR, $0) > stderr;
202	errors++;
203}
204
205function print_version(v)
206{
207	# This function is recursive, so return if this version
208	# has already been printed.  Otherwise, if there is an
209	# ancestral version, recursively print its symbols before
210	# printing the symbols for this version.
211	#
212	if (generated[v] == 1)
213		return;
214	if (successors[v] != "")
215		print_version(successors[v]);
216
217	printf("%s {\n", v);
218
219	# The version count is always one more that actual,
220	# so the loop ranges from 1 to n-1.
221	#
222	for (i = 1; i < versions[v]; i++) {
223		if (i == 1)
224			printf("global:\n");
225		printf("\t%s;\n", symbols[v, i]);
226	}
227
228	version_count--;
229	if (version_count == 0) {
230		printf("local:\n");
231		printf("\t*;\n");
232	}
233	if (successors[v] == "")
234		printf("};\n");
235	else
236		printf("} %s;\n", successors[v]);
237	printf("\n");
238
239	generated[v] = 1;
240    }
241
242END {
243	if (errors) {
244		printf("%d error(s) total.\n", errors) > stderr;
245		exit(1);
246	}
247	# OK, no errors.
248	for (v in versions) {
249		print_version(v);
250	}
251}
252