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