xref: /titanic_53/usr/src/tools/scripts/interface_cmp.pl (revision 75ce41a57ff334bd8fe2cb9ed51eea835892f944)
1*75ce41a5SAli Bahrami#!/usr/bin/perl -w
2*75ce41a5SAli Bahrami#
3*75ce41a5SAli Bahrami# CDDL HEADER START
4*75ce41a5SAli Bahrami#
5*75ce41a5SAli Bahrami# The contents of this file are subject to the terms of the
6*75ce41a5SAli Bahrami# Common Development and Distribution License (the "License").
7*75ce41a5SAli Bahrami# You may not use this file except in compliance with the License.
8*75ce41a5SAli Bahrami#
9*75ce41a5SAli Bahrami# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*75ce41a5SAli Bahrami# or http://www.opensolaris.org/os/licensing.
11*75ce41a5SAli Bahrami# See the License for the specific language governing permissions
12*75ce41a5SAli Bahrami# and limitations under the License.
13*75ce41a5SAli Bahrami#
14*75ce41a5SAli Bahrami# When distributing Covered Code, include this CDDL HEADER in each
15*75ce41a5SAli Bahrami# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*75ce41a5SAli Bahrami# If applicable, add the following below this CDDL HEADER, with the
17*75ce41a5SAli Bahrami# fields enclosed by brackets "[]" replaced with your own identifying
18*75ce41a5SAli Bahrami# information: Portions Copyright [yyyy] [name of copyright owner]
19*75ce41a5SAli Bahrami#
20*75ce41a5SAli Bahrami# CDDL HEADER END
21*75ce41a5SAli Bahrami#
22*75ce41a5SAli Bahrami
23*75ce41a5SAli Bahrami#
24*75ce41a5SAli Bahrami# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25*75ce41a5SAli Bahrami# Use is subject to license terms.
26*75ce41a5SAli Bahrami#
27*75ce41a5SAli Bahrami
28*75ce41a5SAli Bahrami#
29*75ce41a5SAli Bahrami# interface_cmp audits two interface definition files (as created by
30*75ce41a5SAli Bahrami# interface_check) against one another, and confirms that:
31*75ce41a5SAli Bahrami#
32*75ce41a5SAli Bahrami#  o	All versioned libraries that were present in the previous interface
33*75ce41a5SAli Bahrami#	are present in the new interface
34*75ce41a5SAli Bahrami#
35*75ce41a5SAli Bahrami#  o	for each non-private interface in a library confirm that no symbols
36*75ce41a5SAli Bahrami#	have been removed and that no symbols have been added to it between
37*75ce41a5SAli Bahrami#	the two revisions
38*75ce41a5SAli Bahrami#
39*75ce41a5SAli Bahrami# Return codes:
40*75ce41a5SAli Bahrami#
41*75ce41a5SAli Bahrami#  0	All interfaces in the new release are identical in old release.
42*75ce41a5SAli Bahrami#  1	Something is different refer to the error messages.
43*75ce41a5SAli Bahrami
44*75ce41a5SAli Bahrami
45*75ce41a5SAli Bahramiuse strict;
46*75ce41a5SAli Bahrami
47*75ce41a5SAli Bahramiuse POSIX qw(getenv);
48*75ce41a5SAli Bahramiuse Getopt::Std;
49*75ce41a5SAli Bahramiuse File::Basename;
50*75ce41a5SAli Bahrami
51*75ce41a5SAli Bahrami#### Define all global variables (required for strict)
52*75ce41a5SAli Bahramiuse vars  qw($Prog);
53*75ce41a5SAli Bahramiuse vars  qw(%opt);
54*75ce41a5SAli Bahramiuse vars  qw(%old_hash %old_alias %new_hash %new_alias);
55*75ce41a5SAli Bahrami
56*75ce41a5SAli Bahrami# Exception Arrays:
57*75ce41a5SAli Bahrami#
58*75ce41a5SAli Bahrami# The ADDSYM and DELSYM exceptions are maintained on the @AddSymList
59*75ce41a5SAli Bahrami# and @DelSymList arrays, respectively. Each array element is a reference
60*75ce41a5SAli Bahrami# to a subarray of triples:
61*75ce41a5SAli Bahrami#	(sym_re, ver_re, obj_re)
62*75ce41a5SAli Bahrami# where each item in the tripple is a regular expression, used to
63*75ce41a5SAli Bahrami# match a particular symbol/version/object combination.
64*75ce41a5SAli Bahrami#
65*75ce41a5SAli Bahrami# The EMPTY_TOPVERSION exceptions are maintained on the @EmptyTopVerList
66*75ce41a5SAli Bahrami# array. Each array element is a reference to a subarray of pairs:
67*75ce41a5SAli Bahrami#	(ver_re, obj_re)
68*75ce41a5SAli Bahrami# where each item in the pair is a regular expression, used to
69*75ce41a5SAli Bahrami# match a particular version/object combination.
70*75ce41a5SAli Bahrami#
71*75ce41a5SAli Bahramiuse vars  qw(@AddSymList @DelSymList @EmptyTopVerList);
72*75ce41a5SAli Bahrami
73*75ce41a5SAli Bahrami
74*75ce41a5SAli Bahrami## LoadExceptions
75*75ce41a5SAli Bahrami#
76*75ce41a5SAli Bahrami# Locate the exceptions file and process its contents. We can't use
77*75ce41a5SAli Bahrami# onbld_elfmod::LoadExceptionsToEXRE() for this, because our exceptions
78*75ce41a5SAli Bahrami# need to support more than a single regular expression.
79*75ce41a5SAli Bahrami#
80*75ce41a5SAli Bahrami# exit:
81*75ce41a5SAli Bahrami#	@AddSymList, @DelSymList, and @EmptyTopVerList have been updated
82*75ce41a5SAli Bahrami#
83*75ce41a5SAli Bahrami# note:
84*75ce41a5SAli Bahrami#	We expand strings of the form MACH(dir) to match the given
85*75ce41a5SAli Bahrami#	directory as well as any 64-bit architecture subdirectory that
86*75ce41a5SAli Bahrami#	might be present (i.e. amd64, sparcv9).
87*75ce41a5SAli Bahrami#
88*75ce41a5SAli Bahramisub LoadExceptions {
89*75ce41a5SAli Bahrami	my $file;
90*75ce41a5SAli Bahrami	my $Line;
91*75ce41a5SAli Bahrami	my $LineNum = 0;
92*75ce41a5SAli Bahrami	my $err = 0;
93*75ce41a5SAli Bahrami
94*75ce41a5SAli Bahrami	# Locate the exception file
95*75ce41a5SAli Bahrami	FILE: {
96*75ce41a5SAli Bahrami		# If -e is specified, that file must be used
97*75ce41a5SAli Bahrami		if ($opt{e}) {
98*75ce41a5SAli Bahrami			$file = $opt{e};
99*75ce41a5SAli Bahrami			last FILE;
100*75ce41a5SAli Bahrami		}
101*75ce41a5SAli Bahrami
102*75ce41a5SAli Bahrami		# If this is an activated workspace, use the exception
103*75ce41a5SAli Bahrami		# file found in the exceptions_list directory.
104*75ce41a5SAli Bahrami		if (defined($ENV{CODEMGR_WS})) {
105*75ce41a5SAli Bahrami			$file = "$ENV{CODEMGR_WS}/exception_lists/interface_cmp";
106*75ce41a5SAli Bahrami			last FILE if (-f $file);
107*75ce41a5SAli Bahrami		}
108*75ce41a5SAli Bahrami
109*75ce41a5SAli Bahrami		# As a final backstop, the SUNWonbld package provides a
110*75ce41a5SAli Bahrami		# copy of the exception file. This can be useful if we
111*75ce41a5SAli Bahrami		# are being used with an older workspace.
112*75ce41a5SAli Bahrami		#
113*75ce41a5SAli Bahrami		# This script is installed in the SUNWonbld bin directory,
114*75ce41a5SAli Bahrami		# while the exception file is in etc/exception_lists. Find
115*75ce41a5SAli Bahrami		# it relative to the script location given by $0.
116*75ce41a5SAli Bahrami		$file = dirname($0) . "/../etc/exception_lists/interface_cmp";
117*75ce41a5SAli Bahrami		last FILE if (-f $file);
118*75ce41a5SAli Bahrami
119*75ce41a5SAli Bahrami		# No exception file was found.
120*75ce41a5SAli Bahrami		return;
121*75ce41a5SAli Bahrami	}
122*75ce41a5SAli Bahrami
123*75ce41a5SAli Bahrami	open (EFILE, $file) ||
124*75ce41a5SAli Bahrami		die "$Prog: unable to open exceptions file: $file";
125*75ce41a5SAli Bahrami	while ($Line = onbld_elfmod::GetLine(\*EFILE, \$LineNum)) {
126*75ce41a5SAli Bahrami
127*75ce41a5SAli Bahrami		# Expand MACH()
128*75ce41a5SAli Bahrami		$Line =~ s/MACH\(([^)]+)\)/$1(\/amd64|\/sparcv9)?/g;
129*75ce41a5SAli Bahrami
130*75ce41a5SAli Bahrami		if ($Line =~ /^DELSYM\s+/) {
131*75ce41a5SAli Bahrami		    my ($item, $sym_re, $ver_re, $obj_re) =
132*75ce41a5SAli Bahrami			split(/\s+/, $Line, 4);
133*75ce41a5SAli Bahrami		    push @DelSymList, [ $sym_re, $ver_re, $obj_re ];
134*75ce41a5SAli Bahrami		    next;
135*75ce41a5SAli Bahrami		}
136*75ce41a5SAli Bahrami
137*75ce41a5SAli Bahrami		if ($Line =~ /^ADDSYM\s+/) {
138*75ce41a5SAli Bahrami		    my ($item, $sym_re, $ver_re, $obj_re) =
139*75ce41a5SAli Bahrami			split(/\s+/, $Line, 4);
140*75ce41a5SAli Bahrami		    push @AddSymList, [ $sym_re, $ver_re, $obj_re ];
141*75ce41a5SAli Bahrami		    next;
142*75ce41a5SAli Bahrami		}
143*75ce41a5SAli Bahrami
144*75ce41a5SAli Bahrami		if ($Line =~ /^EMPTY_TOPVERSION\s+/) {
145*75ce41a5SAli Bahrami		    my ($item, $ver_re, $obj_re) = split(/\s+/, $Line, 3);
146*75ce41a5SAli Bahrami		    push @EmptyTopVerList, [ $ver_re, $obj_re ];
147*75ce41a5SAli Bahrami		    next;
148*75ce41a5SAli Bahrami		}
149*75ce41a5SAli Bahrami
150*75ce41a5SAli Bahrami		$err++;
151*75ce41a5SAli Bahrami		printf(STDERR "$file: Unrecognized option: ".
152*75ce41a5SAli Bahrami		    "line $LineNum: $Line\n");
153*75ce41a5SAli Bahrami	}
154*75ce41a5SAli Bahrami	close EFILE;
155*75ce41a5SAli Bahrami
156*75ce41a5SAli Bahrami	exit 1 if ($err != 0);
157*75ce41a5SAli Bahrami}
158*75ce41a5SAli Bahrami
159*75ce41a5SAli Bahrami## ExSym(SymList, sym, ver, obj)
160*75ce41a5SAli Bahrami#
161*75ce41a5SAli Bahrami# Compare a given symbol/version/object combination against the
162*75ce41a5SAli Bahrami# exceptions found in the given list.
163*75ce41a5SAli Bahrami#
164*75ce41a5SAli Bahrami# entry:
165*75ce41a5SAli Bahrami#	SymList - Reference to @AddSymList, or @DelSymList.
166*75ce41a5SAli Bahrami#	sym, ver, obj - Combination to be compared against exception list
167*75ce41a5SAli Bahrami#
168*75ce41a5SAli Bahrami# exit:
169*75ce41a5SAli Bahrami#	Returns True (1) if there is a match, and False (0) otherwise.
170*75ce41a5SAli Bahrami#
171*75ce41a5SAli Bahramisub ExSym {
172*75ce41a5SAli Bahrami	my ($SymList, $sym, $ver, $obj) = @_;
173*75ce41a5SAli Bahrami
174*75ce41a5SAli Bahrami	foreach my $ex (@$SymList) {
175*75ce41a5SAli Bahrami		return 1 if ($obj =~ /$$ex[2]/) && ($ver =~ /$$ex[1]/) &&
176*75ce41a5SAli Bahrami		    ($sym =~ /$$ex[0]/);
177*75ce41a5SAli Bahrami	}
178*75ce41a5SAli Bahrami
179*75ce41a5SAli Bahrami	return 0;
180*75ce41a5SAli Bahrami}
181*75ce41a5SAli Bahrami
182*75ce41a5SAli Bahrami## ExTopVer(ver, obj)
183*75ce41a5SAli Bahrami#
184*75ce41a5SAli Bahrami# Compare a given version/object combination against the pairs found
185*75ce41a5SAli Bahrami# in @EmptyTopVerList.
186*75ce41a5SAli Bahrami#
187*75ce41a5SAli Bahrami# entry:
188*75ce41a5SAli Bahrami#	ver, obj - Combination to be compared against empty top version list
189*75ce41a5SAli Bahrami#
190*75ce41a5SAli Bahrami# exit:
191*75ce41a5SAli Bahrami#	Returns True (1) if there is a match, and False (0) otherwise.
192*75ce41a5SAli Bahrami#
193*75ce41a5SAli Bahramisub ExTopVer {
194*75ce41a5SAli Bahrami	my ($ver, $obj) = @_;
195*75ce41a5SAli Bahrami
196*75ce41a5SAli Bahrami	foreach my $ex (@EmptyTopVerList) {
197*75ce41a5SAli Bahrami		return 1 if ($obj =~ /$$ex[1]/) && ($ver =~ /$$ex[0]/);
198*75ce41a5SAli Bahrami	}
199*75ce41a5SAli Bahrami
200*75ce41a5SAli Bahrami	return 0;
201*75ce41a5SAli Bahrami}
202*75ce41a5SAli Bahrami
203*75ce41a5SAli Bahrami## ExpandInheritance(objhashref)
204*75ce41a5SAli Bahrami#
205*75ce41a5SAli Bahrami# For each version contained in the specified object hash reference,
206*75ce41a5SAli Bahrami# add the inherited symbols.
207*75ce41a5SAli Bahrami#
208*75ce41a5SAli Bahramisub ExpandInheritance {
209*75ce41a5SAli Bahrami	my $obj = $_[0];
210*75ce41a5SAli Bahrami
211*75ce41a5SAli Bahrami	# Versions to process. Typically, inheriting versions come before
212*75ce41a5SAli Bahrami	# the versions they inherit. Processing the list in reverse order
213*75ce41a5SAli Bahrami	# maximizes the odds that a needed sub-version will have already
214*75ce41a5SAli Bahrami	# have been processed.
215*75ce41a5SAli Bahrami	my @vers = reverse(@{$obj->{'VERSION_NAMES'}});
216*75ce41a5SAli Bahrami
217*75ce41a5SAli Bahrami	# Versions to process in the next pass
218*75ce41a5SAli Bahrami	my @next_vers = ();
219*75ce41a5SAli Bahrami
220*75ce41a5SAli Bahrami	# Hash, indexed by version name, that reflects whether the version
221*75ce41a5SAli Bahrami	# has been expanded yet or not.
222*75ce41a5SAli Bahrami	my %done = ();
223*75ce41a5SAli Bahrami
224*75ce41a5SAli Bahrami	while (scalar(@vers) > 0) {
225*75ce41a5SAli Bahrami		foreach my $name (@vers) {
226*75ce41a5SAli Bahrami			my $i;
227*75ce41a5SAli Bahrami			my $defer = 0;
228*75ce41a5SAli Bahrami			my $cur_version = $obj->{'VERSION_INFO'}{$name};
229*75ce41a5SAli Bahrami			my ($top, $direct, $total, $symhash, $inheritarr) =
230*75ce41a5SAli Bahrami			    @{$cur_version};
231*75ce41a5SAli Bahrami
232*75ce41a5SAli Bahrami			# In order to expand this version, all the inherited
233*75ce41a5SAli Bahrami			# versions must already have been done. If not, put
234*75ce41a5SAli Bahrami			# this version on @next_vers for the next pass.
235*75ce41a5SAli Bahrami			my $num = scalar(@$inheritarr);
236*75ce41a5SAli Bahrami			for ($i = 0; $i < $num; $i++) {
237*75ce41a5SAli Bahrami			    if (!$done{$inheritarr->[$i]}) {
238*75ce41a5SAli Bahrami				$defer = 1;
239*75ce41a5SAli Bahrami				push @next_vers, $name;
240*75ce41a5SAli Bahrami				last;
241*75ce41a5SAli Bahrami			    }
242*75ce41a5SAli Bahrami			}
243*75ce41a5SAli Bahrami			next if ($defer);
244*75ce41a5SAli Bahrami
245*75ce41a5SAli Bahrami			# Add all the symbols from the inherited versions
246*75ce41a5SAli Bahrami			# to this one.
247*75ce41a5SAli Bahrami			for ($i = 0; $i < $num; $i++) {
248*75ce41a5SAli Bahrami				my $i_version =
249*75ce41a5SAli Bahrami				    $obj->{'VERSION_INFO'}{$inheritarr->[$i]};
250*75ce41a5SAli Bahrami				my $i_symhash = $i_version->[3];
251*75ce41a5SAli Bahrami
252*75ce41a5SAli Bahrami				foreach my $sym (keys %$i_symhash) {
253*75ce41a5SAli Bahrami				    if (!defined($cur_version->[3]{$sym})) {
254*75ce41a5SAli Bahrami					    $cur_version->[2]++;
255*75ce41a5SAli Bahrami					    $cur_version->[3]{$sym} = 'INHERIT';
256*75ce41a5SAli Bahrami				    }
257*75ce41a5SAli Bahrami				}
258*75ce41a5SAli Bahrami			}
259*75ce41a5SAli Bahrami
260*75ce41a5SAli Bahrami			$done{$name} = 1;
261*75ce41a5SAli Bahrami		}
262*75ce41a5SAli Bahrami
263*75ce41a5SAli Bahrami		@vers = @next_vers;
264*75ce41a5SAli Bahrami		@next_vers = ();
265*75ce41a5SAli Bahrami	}
266*75ce41a5SAli Bahrami}
267*75ce41a5SAli Bahrami
268*75ce41a5SAli Bahrami## ReadInterface(file, alias)
269*75ce41a5SAli Bahrami#
270*75ce41a5SAli Bahrami# Read the interface description file, as produced by interface_check, and
271*75ce41a5SAli Bahrami# return a hash describing it.
272*75ce41a5SAli Bahrami#
273*75ce41a5SAli Bahrami# entry:
274*75ce41a5SAli Bahrami#	file - Interface file to read.
275*75ce41a5SAli Bahrami#	alias - Refence to hash to be filled in with any aliases
276*75ce41a5SAli Bahrami#		that are seen in the file. The alias name is the key,
277*75ce41a5SAli Bahrami#		and the object is the value.
278*75ce41a5SAli Bahrami#
279*75ce41a5SAli Bahrami# exit:
280*75ce41a5SAli Bahrami#	The hash referenced by alias has been updated.
281*75ce41a5SAli Bahrami#
282*75ce41a5SAli Bahrami#	The return value is a hash that encapsulates the interface
283*75ce41a5SAli Bahrami#	information. This hash returned uses the object names as the
284*75ce41a5SAli Bahrami#	key. Each key references a sub-hash that contains information
285*75ce41a5SAli Bahrami#	for that object:
286*75ce41a5SAli Bahrami#
287*75ce41a5SAli Bahrami#	CLASS		-> ELFCLASS
288*75ce41a5SAli Bahrami#	TYPE		-> ELF type
289*75ce41a5SAli Bahrami#	VERSION_NAMES	-> Reference to array [1..n] of version names, in the
290*75ce41a5SAli Bahrami#			   order they come from the input file.
291*75ce41a5SAli Bahrami#	VERSION_INFO	-> Reference to hash indexed by version name, yielding
292*75ce41a5SAli Bahrami#			   a reference to an array containing information about
293*75ce41a5SAli Bahrami#			   that version.
294*75ce41a5SAli Bahrami#
295*75ce41a5SAli Bahrami#	The arrays referenced via VERSION_INFO are of the form:
296*75ce41a5SAli Bahrami#
297*75ce41a5SAli Bahrami#		(top, new, total, symhashref, inheritarrref)
298*75ce41a5SAli Bahrami#
299*75ce41a5SAli Bahrami#	where:
300*75ce41a5SAli Bahrami#		top - 1 if version is a TOP_VERSION, 0 for a regular VERSION
301*75ce41a5SAli Bahrami#		new - Number of symbols defined explicitly by version
302*75ce41a5SAli Bahrami#		total - Number of symbols included in version, both new,
303*75ce41a5SAli Bahrami#			and via inheritance.
304*75ce41a5SAli Bahrami#		symhashref - Reference to hash indexed by symbol names, and
305*75ce41a5SAli Bahrami#			yielding true (1).
306*75ce41a5SAli Bahrami#		inheritarrref - Reference to array of names of versions
307*75ce41a5SAli Bahrami#			inherited by this one.
308*75ce41a5SAli Bahrami#
309*75ce41a5SAli Bahramisub ReadInterface {
310*75ce41a5SAli Bahrami	my ($file, $alias) = @_;
311*75ce41a5SAli Bahrami	my %main_hash = ();
312*75ce41a5SAli Bahrami	my $Line;
313*75ce41a5SAli Bahrami	my $LineNum = 0;
314*75ce41a5SAli Bahrami	my $obj_name;
315*75ce41a5SAli Bahrami	my $obj_hash;
316*75ce41a5SAli Bahrami	my $sym_ok = 0;
317*75ce41a5SAli Bahrami	my $cur_version;
318*75ce41a5SAli Bahrami
319*75ce41a5SAli Bahrami	open(FILE, $file) || die "$Prog: Unable to open: $file";
320*75ce41a5SAli Bahrami
321*75ce41a5SAli Bahrami	# Until we see an OBJECT line, nothing else is valid. To
322*75ce41a5SAli Bahrami	# simplify the error handling, use a simple initial loop to
323*75ce41a5SAli Bahrami	# read the file up to that point
324*75ce41a5SAli Bahrami	while ($Line = onbld_elfmod::GetLine(\*FILE, \$LineNum)) {
325*75ce41a5SAli Bahrami		if ($Line =~ s/^OBJECT\s+//i) {
326*75ce41a5SAli Bahrami		    $obj_name = $Line;
327*75ce41a5SAli Bahrami		    $main_hash{$obj_name} = {};
328*75ce41a5SAli Bahrami		    $obj_hash = $main_hash{$obj_name};
329*75ce41a5SAli Bahrami		    last;
330*75ce41a5SAli Bahrami		}
331*75ce41a5SAli Bahrami		die "$file: OBJECT expected on line $LineNum: $Line\n";
332*75ce41a5SAli Bahrami	}
333*75ce41a5SAli Bahrami
334*75ce41a5SAli Bahrami	# Read the remainder of the file
335*75ce41a5SAli Bahrami	while ($Line = onbld_elfmod::GetLine(\*FILE, \$LineNum)) {
336*75ce41a5SAli Bahrami		# Items are parsed in order of decreasing frequency
337*75ce41a5SAli Bahrami
338*75ce41a5SAli Bahrami		if ($Line =~
339*75ce41a5SAli Bahrami		    /^SYMBOL\s+([^\s]+)$/i) {
340*75ce41a5SAli Bahrami			my $sym = $1;
341*75ce41a5SAli Bahrami
342*75ce41a5SAli Bahrami			die "$file: SYMBOL not expected on line $LineNum: $Line\n"
343*75ce41a5SAli Bahrami			    if !$sym_ok;
344*75ce41a5SAli Bahrami
345*75ce41a5SAli Bahrami			$cur_version->[1]++;
346*75ce41a5SAli Bahrami			$cur_version->[2]++;
347*75ce41a5SAli Bahrami			$cur_version->[3]{$sym} = 'NEW';
348*75ce41a5SAli Bahrami			next;
349*75ce41a5SAli Bahrami		}
350*75ce41a5SAli Bahrami
351*75ce41a5SAli Bahrami		if ($Line =~ /^((TOP_)?VERSION)\s+([^\s]+)(\s+\{(.*)\})?\s*$/i) {
352*75ce41a5SAli Bahrami			my ($top, $name, $inherit) = ($2, $3, $5);
353*75ce41a5SAli Bahrami
354*75ce41a5SAli Bahrami			$top = defined($top) ? 1 : 0;
355*75ce41a5SAli Bahrami
356*75ce41a5SAli Bahrami			my @inheritarr = defined($inherit) ?
357*75ce41a5SAli Bahrami			    split /[,{\s]+/, $inherit : ();
358*75ce41a5SAli Bahrami
359*75ce41a5SAli Bahrami			$cur_version = [ $top, 0, 0, {}, \@inheritarr ];
360*75ce41a5SAli Bahrami			$obj_hash->{'VERSION_INFO'}{$name} = $cur_version;
361*75ce41a5SAli Bahrami
362*75ce41a5SAli Bahrami			push @{$obj_hash->{'VERSION_NAMES'}}, $name;
363*75ce41a5SAli Bahrami			$sym_ok = 1;
364*75ce41a5SAli Bahrami			next;
365*75ce41a5SAli Bahrami		}
366*75ce41a5SAli Bahrami
367*75ce41a5SAli Bahrami		if ($Line =~ /^OBJECT\s+([^\s]+)$/i) {
368*75ce41a5SAli Bahrami		    my $prev_obj_hash = $obj_hash;
369*75ce41a5SAli Bahrami		    $obj_name = $1;
370*75ce41a5SAli Bahrami		    $main_hash{$obj_name} = {};
371*75ce41a5SAli Bahrami		    $obj_hash = $main_hash{$obj_name};
372*75ce41a5SAli Bahrami
373*75ce41a5SAli Bahrami		    # Expand the versions for the object just processed
374*75ce41a5SAli Bahrami		    ExpandInheritance($prev_obj_hash);
375*75ce41a5SAli Bahrami		    next;
376*75ce41a5SAli Bahrami		}
377*75ce41a5SAli Bahrami
378*75ce41a5SAli Bahrami		if ($Line =~ /^CLASS\s+([^\s]+)$/i) {
379*75ce41a5SAli Bahrami			$obj_hash->{'CLASS'} = $1;
380*75ce41a5SAli Bahrami			next;
381*75ce41a5SAli Bahrami		}
382*75ce41a5SAli Bahrami
383*75ce41a5SAli Bahrami		if ($Line =~ /^TYPE\s+([^\s]+)$/i) {
384*75ce41a5SAli Bahrami			$obj_hash->{'TYPE'} = $1;
385*75ce41a5SAli Bahrami			next;
386*75ce41a5SAli Bahrami		}
387*75ce41a5SAli Bahrami
388*75ce41a5SAli Bahrami		if ($Line =~ /^ALIAS\s+([^\s]+)$/i) {
389*75ce41a5SAli Bahrami			$$alias{$1} = $obj_name;
390*75ce41a5SAli Bahrami			next;
391*75ce41a5SAli Bahrami		}
392*75ce41a5SAli Bahrami
393*75ce41a5SAli Bahrami		die "$file: unrecognized item on line $LineNum: $Line\n";
394*75ce41a5SAli Bahrami	}
395*75ce41a5SAli Bahrami	close FILE;
396*75ce41a5SAli Bahrami
397*75ce41a5SAli Bahrami	# Expand the versions for the final object from the file
398*75ce41a5SAli Bahrami	ExpandInheritance($obj_hash);
399*75ce41a5SAli Bahrami
400*75ce41a5SAli Bahrami	return %main_hash;
401*75ce41a5SAli Bahrami}
402*75ce41a5SAli Bahrami
403*75ce41a5SAli Bahrami## PrintInterface(main_hash, alias)
404*75ce41a5SAli Bahrami#
405*75ce41a5SAli Bahrami# Dump the contents of main_hash and alias to stdout in the same format
406*75ce41a5SAli Bahrami# used by interface_check to produce the input interface file. This output
407*75ce41a5SAli Bahrami# should diff cleanly against the original (ignoring the header comments).
408*75ce41a5SAli Bahrami#
409*75ce41a5SAli Bahramisub PrintInterface {
410*75ce41a5SAli Bahrami	my ($main_hash, $alias_hash) = @_;
411*75ce41a5SAli Bahrami
412*75ce41a5SAli Bahrami	foreach my $obj (sort keys %$main_hash) {
413*75ce41a5SAli Bahrami		print "OBJECT\t$obj\n";
414*75ce41a5SAli Bahrami		print "CLASS\t$main_hash->{$obj}{'CLASS'}\n";
415*75ce41a5SAli Bahrami		print "TYPE\t$main_hash->{$obj}{'TYPE'}\n";
416*75ce41a5SAli Bahrami
417*75ce41a5SAli Bahrami		# This is inefficient, but good enough for debugging
418*75ce41a5SAli Bahrami		# Look at all the aliases and print those that belong
419*75ce41a5SAli Bahrami		# to this object.
420*75ce41a5SAli Bahrami		foreach my $alias (sort keys %$alias_hash) {
421*75ce41a5SAli Bahrami			print "ALIAS\t$alias\n"
422*75ce41a5SAli Bahrami			    if ($obj eq $alias_hash->{$alias});
423*75ce41a5SAli Bahrami		}
424*75ce41a5SAli Bahrami
425*75ce41a5SAli Bahrami		next if !defined($main_hash->{$obj}{'VERSION_NAMES'});
426*75ce41a5SAli Bahrami
427*75ce41a5SAli Bahrami		my $num = scalar(@{$main_hash->{$obj}{'VERSION_NAMES'}});
428*75ce41a5SAli Bahrami		my $i;
429*75ce41a5SAli Bahrami		for ($i = 0; $i < $num; $i++) {
430*75ce41a5SAli Bahrami			my $name = $main_hash->{$obj}{'VERSION_NAMES'}[$i];
431*75ce41a5SAli Bahrami			my ($top, $direct, $total, $symhash, $inheritarr) =
432*75ce41a5SAli Bahrami			    @{$main_hash->{$obj}{'VERSION_INFO'}{$name}};
433*75ce41a5SAli Bahrami
434*75ce41a5SAli Bahrami			$top = $top ? "TOP_" : '';
435*75ce41a5SAli Bahrami
436*75ce41a5SAli Bahrami			my $inherit = (scalar(@$inheritarr) > 0) ?
437*75ce41a5SAli Bahrami			    "\t{" . join(', ', @{$inheritarr}) . "}" : '';
438*75ce41a5SAli Bahrami
439*75ce41a5SAli Bahrami			print "${top}VERSION\t$name$inherit\n";
440*75ce41a5SAli Bahrami
441*75ce41a5SAli Bahrami			foreach my $sym (sort keys %$symhash) {
442*75ce41a5SAli Bahrami				print "\t$symhash->{$sym}\t$sym\n";
443*75ce41a5SAli Bahrami			}
444*75ce41a5SAli Bahrami		}
445*75ce41a5SAli Bahrami	}
446*75ce41a5SAli Bahrami}
447*75ce41a5SAli Bahrami
448*75ce41a5SAli Bahrami## compare()
449*75ce41a5SAli Bahrami#
450*75ce41a5SAli Bahrami# Compare the old interface definition contained in (%old_hash, %old_alias)
451*75ce41a5SAli Bahrami# with the new interface contained in (%new_hash, %new_alias).
452*75ce41a5SAli Bahrami#
453*75ce41a5SAli Bahramisub compare {
454*75ce41a5SAli Bahrami	foreach my $old_obj (sort keys %old_hash) {
455*75ce41a5SAli Bahrami		my $new_obj = $old_obj;
456*75ce41a5SAli Bahrami		my $Ttl = 0;
457*75ce41a5SAli Bahrami
458*75ce41a5SAli Bahrami		# If the object does not exist in the new interface,
459*75ce41a5SAli Bahrami		# then see if there's an alias for it. Failing that,
460*75ce41a5SAli Bahrami		# we simply ignore the object.
461*75ce41a5SAli Bahrami		if (!defined($new_hash{$new_obj})) {
462*75ce41a5SAli Bahrami			next if !defined($new_alias{$new_obj});
463*75ce41a5SAli Bahrami			$new_obj = $new_alias{$new_obj};
464*75ce41a5SAli Bahrami		}
465*75ce41a5SAli Bahrami
466*75ce41a5SAli Bahrami		my $old = $old_hash{$old_obj};
467*75ce41a5SAli Bahrami		my $new = $new_hash{$new_obj};
468*75ce41a5SAli Bahrami
469*75ce41a5SAli Bahrami		# Every version in the old object must exist in the new object,
470*75ce41a5SAli Bahrami		# and there must be exactly the same symbols in each.
471*75ce41a5SAli Bahrami		my $num = scalar(@{$old->{'VERSION_NAMES'}});
472*75ce41a5SAli Bahrami		for (my $i = 0; $i < $num; $i++) {
473*75ce41a5SAli Bahrami			my $name = $old->{'VERSION_NAMES'}[$i];
474*75ce41a5SAli Bahrami
475*75ce41a5SAli Bahrami			# New object must have this version
476*75ce41a5SAli Bahrami			if (!defined($new->{'VERSION_INFO'}{$name})) {
477*75ce41a5SAli Bahrami				onbld_elfmod::OutMsg2(\*STDOUT, \$Ttl, $old_obj,
478*75ce41a5SAli Bahrami				    $new_obj, "$name: deleted version");
479*75ce41a5SAli Bahrami				next;
480*75ce41a5SAli Bahrami			}
481*75ce41a5SAli Bahrami
482*75ce41a5SAli Bahrami			my ($old_top, $old_direct, $old_total, $old_symhash) =
483*75ce41a5SAli Bahrami			    @{$old->{'VERSION_INFO'}{$name}};
484*75ce41a5SAli Bahrami			my ($new_top, $new_direct, $new_total, $new_symhash) =
485*75ce41a5SAli Bahrami			    @{$new->{'VERSION_INFO'}{$name}};
486*75ce41a5SAli Bahrami
487*75ce41a5SAli Bahrami			# If this is an empty top version, and the old object
488*75ce41a5SAli Bahrami			# has the EMPTY_TOPVERSION exception set, then we
489*75ce41a5SAli Bahrami			# skip it as if it were not present.
490*75ce41a5SAli Bahrami			next if $old_top && ($old_direct == 0) &&
491*75ce41a5SAli Bahrami			    ExTopVer($name, $old_obj);
492*75ce41a5SAli Bahrami
493*75ce41a5SAli Bahrami			# We check that every symbol in the old object is
494*75ce41a5SAli Bahrami			# in the new one to detect deleted symbols. We then
495*75ce41a5SAli Bahrami			# check that every symbol in the new object is also
496*75ce41a5SAli Bahrami			# in the old object, to find added symbols. If the
497*75ce41a5SAli Bahrami			# "deleted" check is clean, and the two objects have
498*75ce41a5SAli Bahrami			# the same number of symbols in their versions, then we
499*75ce41a5SAli Bahrami			# can skip the "added" test, because we know that
500*75ce41a5SAli Bahrami			# there is no room for an addition to have happened.
501*75ce41a5SAli Bahrami			# Since most objects satisfy these constraints, we
502*75ce41a5SAli Bahrami			# end up doing roughly half the number of comparisons
503*75ce41a5SAli Bahrami			# that would otherwise be needed.
504*75ce41a5SAli Bahrami			my $check_added_syms =
505*75ce41a5SAli Bahrami			    ($old_total == $new_total) ? 0: 1;
506*75ce41a5SAli Bahrami
507*75ce41a5SAli Bahrami			# Every symbol in the old version must be in the new one
508*75ce41a5SAli Bahrami			foreach my $sym (sort keys %$old_symhash) {
509*75ce41a5SAli Bahrami				if (!defined($new_symhash->{$sym})) {
510*75ce41a5SAli Bahrami					onbld_elfmod::OutMsg2(\*STDOUT,
511*75ce41a5SAli Bahrami					   \$Ttl, $old_obj, $new_obj,
512*75ce41a5SAli Bahrami					   "$name: deleted interface: $sym")
513*75ce41a5SAli Bahrami					    if !ExSym(\@DelSymList,
514*75ce41a5SAli Bahrami						      $sym, $name, $new_obj);
515*75ce41a5SAli Bahrami					$check_added_syms = 1;
516*75ce41a5SAli Bahrami				}
517*75ce41a5SAli Bahrami			}
518*75ce41a5SAli Bahrami
519*75ce41a5SAli Bahrami			# Do the "added" check, unless we can optimize it away.
520*75ce41a5SAli Bahrami			# Every symbol in the new version must be in the old one.
521*75ce41a5SAli Bahrami			if ($check_added_syms) {
522*75ce41a5SAli Bahrami				foreach my $sym (sort keys %$new_symhash) {
523*75ce41a5SAli Bahrami				    if (!defined($old_symhash->{$sym})) {
524*75ce41a5SAli Bahrami					next if ExSym(\@AddSymList,
525*75ce41a5SAli Bahrami					    $sym, $name, $new_obj);
526*75ce41a5SAli Bahrami					onbld_elfmod::OutMsg2(\*STDOUT,
527*75ce41a5SAli Bahrami					       \$Ttl, $old_obj, $new_obj,
528*75ce41a5SAli Bahrami					       "$name: added interface: $sym");
529*75ce41a5SAli Bahrami				    }
530*75ce41a5SAli Bahrami				}
531*75ce41a5SAli Bahrami			}
532*75ce41a5SAli Bahrami
533*75ce41a5SAli Bahrami			# We want to ensure that version numbers in an
534*75ce41a5SAli Bahrami			# inheritance chain don't go up by more than 1 in
535*75ce41a5SAli Bahrami			# any given release. If the version names are in the
536*75ce41a5SAli Bahrami			# standard SUNW_x.y[.z] format, we can compare the
537*75ce41a5SAli Bahrami			# two top versions and see if this has happened.
538*75ce41a5SAli Bahrami			#
539*75ce41a5SAli Bahrami			# For a given SUNW_x.y[.z], valid sucessors would
540*75ce41a5SAli Bahrami			# be SUNW_x.(y+1) or SUNW_x.y.(z+1), where z is
541*75ce41a5SAli Bahrami			# assumed to be 0 if not present.
542*75ce41a5SAli Bahrami			#
543*75ce41a5SAli Bahrami			# This check only makes sense when the new interface
544*75ce41a5SAli Bahrami			# is a direct decendent of the old one, as specified
545*75ce41a5SAli Bahrami			# via the -d option. If the two interfaces are more
546*75ce41a5SAli Bahrami			# than one release apart, we should not do this test.
547*75ce41a5SAli Bahrami			if ($opt{d} && $old_top && !$new_top &&
548*75ce41a5SAli Bahrami			    ($name =~ /^SUNW_(\d+)\.(\d+)(\.(\d+))?/)) {
549*75ce41a5SAli Bahrami				my $iname1 = "SUNW_$1." . ($2 + 1);
550*75ce41a5SAli Bahrami				my $iname2;
551*75ce41a5SAli Bahrami				if (defined($4)) {
552*75ce41a5SAli Bahrami			    		$iname2 = "SUNW_$1.$2." . ($4 + 1);
553*75ce41a5SAli Bahrami				} else {
554*75ce41a5SAli Bahrami			    		$iname2 = "SUNW_$1.$2.1";
555*75ce41a5SAli Bahrami				}
556*75ce41a5SAli Bahrami
557*75ce41a5SAli Bahrami				if (defined($new->{'VERSION_INFO'}{$iname1}) ||
558*75ce41a5SAli Bahrami				    defined($new->{'VERSION_INFO'}{$iname2})) {
559*75ce41a5SAli Bahrami					my $i_top =
560*75ce41a5SAli Bahrami					    $new->{'VERSION_INFO'}{$iname1}[0] ||
561*75ce41a5SAli Bahrami					    $new->{'VERSION_INFO'}{$iname2}[0];
562*75ce41a5SAli Bahrami					if (!$i_top) {
563*75ce41a5SAli Bahrami						onbld_elfmod::OutMsg2(\*STDOUT,
564*75ce41a5SAli Bahrami						    \$Ttl, $old_obj, $new_obj,
565*75ce41a5SAli Bahrami						    "$name: inconsistant " .
566*75ce41a5SAli Bahrami						    "version increment: " .
567*75ce41a5SAli Bahrami						    "expect $iname1 or $iname2 ".
568*75ce41a5SAli Bahrami						    "to replace top version");
569*75ce41a5SAli Bahrami					}
570*75ce41a5SAli Bahrami				} else {
571*75ce41a5SAli Bahrami 					onbld_elfmod::OutMsg2(\*STDOUT,
572*75ce41a5SAli Bahrami					    \$Ttl, $old_obj, $new_obj,
573*75ce41a5SAli Bahrami				            "$name: expected superseding " .
574*75ce41a5SAli Bahrami					    "top version to $name not " .
575*75ce41a5SAli Bahrami					    "present: $iname1 or $iname2");
576*75ce41a5SAli Bahrami				}
577*75ce41a5SAli Bahrami			}
578*75ce41a5SAli Bahrami		}
579*75ce41a5SAli Bahrami
580*75ce41a5SAli Bahrami
581*75ce41a5SAli Bahrami		# Empty versions in the established interface description
582*75ce41a5SAli Bahrami		# are usually the result of fixing a versioning mistake
583*75ce41a5SAli Bahrami		# at some point in the past. These versions are part of
584*75ce41a5SAli Bahrami		# the public record, and cannot be changed now. However, if
585*75ce41a5SAli Bahrami		# comparing two interface descriptions from the same gate,
586*75ce41a5SAli Bahrami		# flag any empty versions in the new interface description
587*75ce41a5SAli Bahrami		# that are not present in the old one. These have yet to
588*75ce41a5SAli Bahrami		# become part of the official interface, and should be removed
589*75ce41a5SAli Bahrami		# before they do.
590*75ce41a5SAli Bahrami		next if !$opt{d};
591*75ce41a5SAli Bahrami
592*75ce41a5SAli Bahrami		$num = scalar(@{$new->{'VERSION_NAMES'}});
593*75ce41a5SAli Bahrami		for (my $i = 0; $i < $num; $i++) {
594*75ce41a5SAli Bahrami			my $name = $new->{'VERSION_NAMES'}[$i];
595*75ce41a5SAli Bahrami
596*75ce41a5SAli Bahrami			# If old object has this version, skip it
597*75ce41a5SAli Bahrami			next if defined($old->{'VERSION_INFO'}{$name});
598*75ce41a5SAli Bahrami
599*75ce41a5SAli Bahrami			# If explicitly whitelisted, skip it
600*75ce41a5SAli Bahrami			next if ExTopVer($name, $new_obj);
601*75ce41a5SAli Bahrami
602*75ce41a5SAli Bahrami			my ($new_top, $new_direct, $new_total, $new_symhash) =
603*75ce41a5SAli Bahrami			    @{$new->{'VERSION_INFO'}{$name}};
604*75ce41a5SAli Bahrami
605*75ce41a5SAli Bahrami			if ($new_direct == 0) {
606*75ce41a5SAli Bahrami				onbld_elfmod::OutMsg2(\*STDOUT,
607*75ce41a5SAli Bahrami				    \$Ttl, $old_obj, $new_obj,
608*75ce41a5SAli Bahrami				    "$name: invalid empty new version");
609*75ce41a5SAli Bahrami			}
610*75ce41a5SAli Bahrami		}
611*75ce41a5SAli Bahrami	}
612*75ce41a5SAli Bahrami
613*75ce41a5SAli Bahrami}
614*75ce41a5SAli Bahrami
615*75ce41a5SAli Bahrami
616*75ce41a5SAli Bahrami
617*75ce41a5SAli Bahrami# -----------------------------------------------------------------------------
618*75ce41a5SAli Bahrami
619*75ce41a5SAli Bahrami# Establish a program name for any error diagnostics.
620*75ce41a5SAli Bahramichomp($Prog = `basename $0`);
621*75ce41a5SAli Bahrami
622*75ce41a5SAli Bahrami# The onbld_elfmod package is maintained in the same directory as this
623*75ce41a5SAli Bahrami# script, and is installed in ../lib/perl. Use the local one if present,
624*75ce41a5SAli Bahrami# and the installed one otherwise.
625*75ce41a5SAli Bahramimy $moddir = dirname($0);
626*75ce41a5SAli Bahrami$moddir = "$moddir/../lib/perl" if ! -f "$moddir/onbld_elfmod.pm";
627*75ce41a5SAli Bahramirequire "$moddir/onbld_elfmod.pm";
628*75ce41a5SAli Bahrami
629*75ce41a5SAli Bahrami# Check that we have arguments. Normally, 2 plain arguments are required,
630*75ce41a5SAli Bahrami# but if -t is present, only one is allowed.
631*75ce41a5SAli Bahramiif ((getopts('de:ot', \%opt) == 0) || (scalar(@ARGV) != ($opt{t} ? 1 : 2))) {
632*75ce41a5SAli Bahrami	print "usage: $Prog [-dot] [-e exfile] old new\n";
633*75ce41a5SAli Bahrami	print "\t[-d]\t\tnew is a direct decendent of old\n";
634*75ce41a5SAli Bahrami	print "\t[-e exfile]\texceptions file\n";
635*75ce41a5SAli Bahrami	print "\t[-o]\t\tproduce one-liner output (prefixed with pathname)\n";
636*75ce41a5SAli Bahrami	print "\t[-t]\tParse old, and recreate to stdout\n";
637*75ce41a5SAli Bahrami	exit 1;
638*75ce41a5SAli Bahrami}
639*75ce41a5SAli Bahrami
640*75ce41a5SAli Bahrami# Locate and process the exceptions file
641*75ce41a5SAli BahramiLoadExceptions();
642*75ce41a5SAli Bahrami
643*75ce41a5SAli Bahrami%old_alias = ();
644*75ce41a5SAli Bahrami%old_hash = ReadInterface($ARGV[0], \%old_alias);
645*75ce41a5SAli Bahrami
646*75ce41a5SAli Bahrami# If -t is present, only one argument is allowed --- we parse it, and then
647*75ce41a5SAli Bahrami# print the same information back to stderr in the same format as the original.
648*75ce41a5SAli Bahrami# This is useful for debugging, to verify that the parsing is correct.
649*75ce41a5SAli Bahramiif ($opt{t}) {
650*75ce41a5SAli Bahrami	PrintInterface(\%old_hash, \%old_alias);
651*75ce41a5SAli Bahrami	exit 0;
652*75ce41a5SAli Bahrami}
653*75ce41a5SAli Bahrami
654*75ce41a5SAli Bahrami%new_alias = ();
655*75ce41a5SAli Bahrami%new_hash = ReadInterface($ARGV[1], \%new_alias);
656*75ce41a5SAli Bahrami
657*75ce41a5SAli Bahramicompare();
658*75ce41a5SAli Bahrami
659*75ce41a5SAli Bahramiexit 0;
660