175ce41a5SAli Bahrami#!/usr/bin/perl -w
275ce41a5SAli Bahrami#
375ce41a5SAli Bahrami# CDDL HEADER START
475ce41a5SAli Bahrami#
575ce41a5SAli Bahrami# The contents of this file are subject to the terms of the
675ce41a5SAli Bahrami# Common Development and Distribution License (the "License").
775ce41a5SAli Bahrami# You may not use this file except in compliance with the License.
875ce41a5SAli Bahrami#
975ce41a5SAli Bahrami# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1075ce41a5SAli Bahrami# or http://www.opensolaris.org/os/licensing.
1175ce41a5SAli Bahrami# See the License for the specific language governing permissions
1275ce41a5SAli Bahrami# and limitations under the License.
1375ce41a5SAli Bahrami#
1475ce41a5SAli Bahrami# When distributing Covered Code, include this CDDL HEADER in each
1575ce41a5SAli Bahrami# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1675ce41a5SAli Bahrami# If applicable, add the following below this CDDL HEADER, with the
1775ce41a5SAli Bahrami# fields enclosed by brackets "[]" replaced with your own identifying
1875ce41a5SAli Bahrami# information: Portions Copyright [yyyy] [name of copyright owner]
1975ce41a5SAli Bahrami#
2075ce41a5SAli Bahrami# CDDL HEADER END
2175ce41a5SAli Bahrami#
2275ce41a5SAli Bahrami
2375ce41a5SAli Bahrami#
24*5253169eSAli Bahrami# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2575ce41a5SAli Bahrami#
2675ce41a5SAli Bahrami
2775ce41a5SAli Bahrami#
2875ce41a5SAli Bahrami# interface_cmp audits two interface definition files (as created by
2975ce41a5SAli Bahrami# interface_check) against one another, and confirms that:
3075ce41a5SAli Bahrami#
3175ce41a5SAli Bahrami#  o	All versioned libraries that were present in the previous interface
3275ce41a5SAli Bahrami#	are present in the new interface
3375ce41a5SAli Bahrami#
3475ce41a5SAli Bahrami#  o	for each non-private interface in a library confirm that no symbols
3575ce41a5SAli Bahrami#	have been removed and that no symbols have been added to it between
3675ce41a5SAli Bahrami#	the two revisions
3775ce41a5SAli Bahrami#
3875ce41a5SAli Bahrami# Return codes:
3975ce41a5SAli Bahrami#
4075ce41a5SAli Bahrami#  0	All interfaces in the new release are identical in old release.
4175ce41a5SAli Bahrami#  1	Something is different refer to the error messages.
4275ce41a5SAli Bahrami
4375ce41a5SAli Bahrami
4475ce41a5SAli Bahramiuse strict;
4575ce41a5SAli Bahrami
4675ce41a5SAli Bahramiuse POSIX qw(getenv);
4775ce41a5SAli Bahramiuse Getopt::Std;
4875ce41a5SAli Bahramiuse File::Basename;
4975ce41a5SAli Bahrami
5075ce41a5SAli Bahrami#### Define all global variables (required for strict)
5175ce41a5SAli Bahramiuse vars  qw($Prog);
5275ce41a5SAli Bahramiuse vars  qw(%opt);
5375ce41a5SAli Bahramiuse vars  qw(%old_hash %old_alias %new_hash %new_alias);
5475ce41a5SAli Bahrami
5575ce41a5SAli Bahrami# Exception Arrays:
5675ce41a5SAli Bahrami#
5775ce41a5SAli Bahrami# The ADDSYM and DELSYM exceptions are maintained on the @AddSymList
5875ce41a5SAli Bahrami# and @DelSymList arrays, respectively. Each array element is a reference
5975ce41a5SAli Bahrami# to a subarray of triples:
6075ce41a5SAli Bahrami#	(sym_re, ver_re, obj_re)
6175ce41a5SAli Bahrami# where each item in the tripple is a regular expression, used to
6275ce41a5SAli Bahrami# match a particular symbol/version/object combination.
6375ce41a5SAli Bahrami#
6475ce41a5SAli Bahrami# The EMPTY_TOPVERSION exceptions are maintained on the @EmptyTopVerList
6575ce41a5SAli Bahrami# array. Each array element is a reference to a subarray of pairs:
6675ce41a5SAli Bahrami#	(ver_re, obj_re)
6775ce41a5SAli Bahrami# where each item in the pair is a regular expression, used to
6875ce41a5SAli Bahrami# match a particular version/object combination.
6975ce41a5SAli Bahrami#
7075ce41a5SAli Bahramiuse vars  qw(@AddSymList @DelSymList @EmptyTopVerList);
7175ce41a5SAli Bahrami
7275ce41a5SAli Bahrami
7375ce41a5SAli Bahrami## LoadExceptions
7475ce41a5SAli Bahrami#
7575ce41a5SAli Bahrami# Locate the exceptions file and process its contents. We can't use
7675ce41a5SAli Bahrami# onbld_elfmod::LoadExceptionsToEXRE() for this, because our exceptions
7775ce41a5SAli Bahrami# need to support more than a single regular expression.
7875ce41a5SAli Bahrami#
7975ce41a5SAli Bahrami# exit:
8075ce41a5SAli Bahrami#	@AddSymList, @DelSymList, and @EmptyTopVerList have been updated
8175ce41a5SAli Bahrami#
8275ce41a5SAli Bahrami# note:
8375ce41a5SAli Bahrami#	We expand strings of the form MACH(dir) to match the given
8475ce41a5SAli Bahrami#	directory as well as any 64-bit architecture subdirectory that
8575ce41a5SAli Bahrami#	might be present (i.e. amd64, sparcv9).
8675ce41a5SAli Bahrami#
8775ce41a5SAli Bahramisub LoadExceptions {
8875ce41a5SAli Bahrami	my $file;
8975ce41a5SAli Bahrami	my $Line;
9075ce41a5SAli Bahrami	my $LineNum = 0;
9175ce41a5SAli Bahrami	my $err = 0;
9275ce41a5SAli Bahrami
9375ce41a5SAli Bahrami	# Locate the exception file
9475ce41a5SAli Bahrami	FILE: {
9575ce41a5SAli Bahrami		# If -e is specified, that file must be used
9675ce41a5SAli Bahrami		if ($opt{e}) {
9775ce41a5SAli Bahrami			$file = $opt{e};
9875ce41a5SAli Bahrami			last FILE;
9975ce41a5SAli Bahrami		}
10075ce41a5SAli Bahrami
10175ce41a5SAli Bahrami		# If this is an activated workspace, use the exception
10275ce41a5SAli Bahrami		# file found in the exceptions_list directory.
10375ce41a5SAli Bahrami		if (defined($ENV{CODEMGR_WS})) {
10475ce41a5SAli Bahrami			$file = "$ENV{CODEMGR_WS}/exception_lists/interface_cmp";
10575ce41a5SAli Bahrami			last FILE if (-f $file);
10675ce41a5SAli Bahrami		}
10775ce41a5SAli Bahrami
10875ce41a5SAli Bahrami		# As a final backstop, the SUNWonbld package provides a
10975ce41a5SAli Bahrami		# copy of the exception file. This can be useful if we
11075ce41a5SAli Bahrami		# are being used with an older workspace.
11175ce41a5SAli Bahrami		#
11275ce41a5SAli Bahrami		# This script is installed in the SUNWonbld bin directory,
11375ce41a5SAli Bahrami		# while the exception file is in etc/exception_lists. Find
11475ce41a5SAli Bahrami		# it relative to the script location given by $0.
11575ce41a5SAli Bahrami		$file = dirname($0) . "/../etc/exception_lists/interface_cmp";
11675ce41a5SAli Bahrami		last FILE if (-f $file);
11775ce41a5SAli Bahrami
11875ce41a5SAli Bahrami		# No exception file was found.
11975ce41a5SAli Bahrami		return;
12075ce41a5SAli Bahrami	}
12175ce41a5SAli Bahrami
12275ce41a5SAli Bahrami	open (EFILE, $file) ||
12375ce41a5SAli Bahrami		die "$Prog: unable to open exceptions file: $file";
12475ce41a5SAli Bahrami	while ($Line = onbld_elfmod::GetLine(\*EFILE, \$LineNum)) {
12575ce41a5SAli Bahrami
12675ce41a5SAli Bahrami		# Expand MACH()
12775ce41a5SAli Bahrami		$Line =~ s/MACH\(([^)]+)\)/$1(\/amd64|\/sparcv9)?/g;
12875ce41a5SAli Bahrami
12975ce41a5SAli Bahrami		if ($Line =~ /^DELSYM\s+/) {
13075ce41a5SAli Bahrami		    my ($item, $sym_re, $ver_re, $obj_re) =
13175ce41a5SAli Bahrami			split(/\s+/, $Line, 4);
13275ce41a5SAli Bahrami		    push @DelSymList, [ $sym_re, $ver_re, $obj_re ];
13375ce41a5SAli Bahrami		    next;
13475ce41a5SAli Bahrami		}
13575ce41a5SAli Bahrami
13675ce41a5SAli Bahrami		if ($Line =~ /^ADDSYM\s+/) {
13775ce41a5SAli Bahrami		    my ($item, $sym_re, $ver_re, $obj_re) =
13875ce41a5SAli Bahrami			split(/\s+/, $Line, 4);
13975ce41a5SAli Bahrami		    push @AddSymList, [ $sym_re, $ver_re, $obj_re ];
14075ce41a5SAli Bahrami		    next;
14175ce41a5SAli Bahrami		}
14275ce41a5SAli Bahrami
14375ce41a5SAli Bahrami		if ($Line =~ /^EMPTY_TOPVERSION\s+/) {
14475ce41a5SAli Bahrami		    my ($item, $ver_re, $obj_re) = split(/\s+/, $Line, 3);
14575ce41a5SAli Bahrami		    push @EmptyTopVerList, [ $ver_re, $obj_re ];
14675ce41a5SAli Bahrami		    next;
14775ce41a5SAli Bahrami		}
14875ce41a5SAli Bahrami
14975ce41a5SAli Bahrami		$err++;
15075ce41a5SAli Bahrami		printf(STDERR "$file: Unrecognized option: ".
15175ce41a5SAli Bahrami		    "line $LineNum: $Line\n");
15275ce41a5SAli Bahrami	}
15375ce41a5SAli Bahrami	close EFILE;
15475ce41a5SAli Bahrami
15575ce41a5SAli Bahrami	exit 1 if ($err != 0);
15675ce41a5SAli Bahrami}
15775ce41a5SAli Bahrami
15875ce41a5SAli Bahrami## ExSym(SymList, sym, ver, obj)
15975ce41a5SAli Bahrami#
16075ce41a5SAli Bahrami# Compare a given symbol/version/object combination against the
16175ce41a5SAli Bahrami# exceptions found in the given list.
16275ce41a5SAli Bahrami#
16375ce41a5SAli Bahrami# entry:
16475ce41a5SAli Bahrami#	SymList - Reference to @AddSymList, or @DelSymList.
16575ce41a5SAli Bahrami#	sym, ver, obj - Combination to be compared against exception list
16675ce41a5SAli Bahrami#
16775ce41a5SAli Bahrami# exit:
16875ce41a5SAli Bahrami#	Returns True (1) if there is a match, and False (0) otherwise.
16975ce41a5SAli Bahrami#
17075ce41a5SAli Bahramisub ExSym {
17175ce41a5SAli Bahrami	my ($SymList, $sym, $ver, $obj) = @_;
17275ce41a5SAli Bahrami
17375ce41a5SAli Bahrami	foreach my $ex (@$SymList) {
17475ce41a5SAli Bahrami		return 1 if ($obj =~ /$$ex[2]/) && ($ver =~ /$$ex[1]/) &&
17575ce41a5SAli Bahrami		    ($sym =~ /$$ex[0]/);
17675ce41a5SAli Bahrami	}
17775ce41a5SAli Bahrami
17875ce41a5SAli Bahrami	return 0;
17975ce41a5SAli Bahrami}
18075ce41a5SAli Bahrami
18175ce41a5SAli Bahrami## ExTopVer(ver, obj)
18275ce41a5SAli Bahrami#
18375ce41a5SAli Bahrami# Compare a given version/object combination against the pairs found
18475ce41a5SAli Bahrami# in @EmptyTopVerList.
18575ce41a5SAli Bahrami#
18675ce41a5SAli Bahrami# entry:
18775ce41a5SAli Bahrami#	ver, obj - Combination to be compared against empty top version list
18875ce41a5SAli Bahrami#
18975ce41a5SAli Bahrami# exit:
19075ce41a5SAli Bahrami#	Returns True (1) if there is a match, and False (0) otherwise.
19175ce41a5SAli Bahrami#
19275ce41a5SAli Bahramisub ExTopVer {
19375ce41a5SAli Bahrami	my ($ver, $obj) = @_;
19475ce41a5SAli Bahrami
19575ce41a5SAli Bahrami	foreach my $ex (@EmptyTopVerList) {
19675ce41a5SAli Bahrami		return 1 if ($obj =~ /$$ex[1]/) && ($ver =~ /$$ex[0]/);
19775ce41a5SAli Bahrami	}
19875ce41a5SAli Bahrami
19975ce41a5SAli Bahrami	return 0;
20075ce41a5SAli Bahrami}
20175ce41a5SAli Bahrami
20275ce41a5SAli Bahrami## ExpandInheritance(objhashref)
20375ce41a5SAli Bahrami#
20475ce41a5SAli Bahrami# For each version contained in the specified object hash reference,
20575ce41a5SAli Bahrami# add the inherited symbols.
20675ce41a5SAli Bahrami#
20775ce41a5SAli Bahramisub ExpandInheritance {
20875ce41a5SAli Bahrami	my $obj = $_[0];
20975ce41a5SAli Bahrami
21075ce41a5SAli Bahrami	# Versions to process. Typically, inheriting versions come before
21175ce41a5SAli Bahrami	# the versions they inherit. Processing the list in reverse order
21275ce41a5SAli Bahrami	# maximizes the odds that a needed sub-version will have already
21375ce41a5SAli Bahrami	# have been processed.
21475ce41a5SAli Bahrami	my @vers = reverse(@{$obj->{'VERSION_NAMES'}});
21575ce41a5SAli Bahrami
21675ce41a5SAli Bahrami	# Versions to process in the next pass
21775ce41a5SAli Bahrami	my @next_vers = ();
21875ce41a5SAli Bahrami
21975ce41a5SAli Bahrami	# Hash, indexed by version name, that reflects whether the version
22075ce41a5SAli Bahrami	# has been expanded yet or not.
22175ce41a5SAli Bahrami	my %done = ();
22275ce41a5SAli Bahrami
22375ce41a5SAli Bahrami	while (scalar(@vers) > 0) {
22475ce41a5SAli Bahrami		foreach my $name (@vers) {
22575ce41a5SAli Bahrami			my $i;
22675ce41a5SAli Bahrami			my $defer = 0;
22775ce41a5SAli Bahrami			my $cur_version = $obj->{'VERSION_INFO'}{$name};
22875ce41a5SAli Bahrami			my ($top, $direct, $total, $symhash, $inheritarr) =
22975ce41a5SAli Bahrami			    @{$cur_version};
23075ce41a5SAli Bahrami
23175ce41a5SAli Bahrami			# In order to expand this version, all the inherited
23275ce41a5SAli Bahrami			# versions must already have been done. If not, put
23375ce41a5SAli Bahrami			# this version on @next_vers for the next pass.
23475ce41a5SAli Bahrami			my $num = scalar(@$inheritarr);
23575ce41a5SAli Bahrami			for ($i = 0; $i < $num; $i++) {
23675ce41a5SAli Bahrami			    if (!$done{$inheritarr->[$i]}) {
23775ce41a5SAli Bahrami				$defer = 1;
23875ce41a5SAli Bahrami				push @next_vers, $name;
23975ce41a5SAli Bahrami				last;
24075ce41a5SAli Bahrami			    }
24175ce41a5SAli Bahrami			}
24275ce41a5SAli Bahrami			next if ($defer);
24375ce41a5SAli Bahrami
24475ce41a5SAli Bahrami			# Add all the symbols from the inherited versions
24575ce41a5SAli Bahrami			# to this one.
24675ce41a5SAli Bahrami			for ($i = 0; $i < $num; $i++) {
24775ce41a5SAli Bahrami				my $i_version =
24875ce41a5SAli Bahrami				    $obj->{'VERSION_INFO'}{$inheritarr->[$i]};
24975ce41a5SAli Bahrami				my $i_symhash = $i_version->[3];
25075ce41a5SAli Bahrami
25175ce41a5SAli Bahrami				foreach my $sym (keys %$i_symhash) {
25275ce41a5SAli Bahrami				    if (!defined($cur_version->[3]{$sym})) {
25375ce41a5SAli Bahrami					    $cur_version->[2]++;
25475ce41a5SAli Bahrami					    $cur_version->[3]{$sym} = 'INHERIT';
25575ce41a5SAli Bahrami				    }
25675ce41a5SAli Bahrami				}
25775ce41a5SAli Bahrami			}
25875ce41a5SAli Bahrami
25975ce41a5SAli Bahrami			$done{$name} = 1;
26075ce41a5SAli Bahrami		}
26175ce41a5SAli Bahrami
26275ce41a5SAli Bahrami		@vers = @next_vers;
26375ce41a5SAli Bahrami		@next_vers = ();
26475ce41a5SAli Bahrami	}
26575ce41a5SAli Bahrami}
26675ce41a5SAli Bahrami
26775ce41a5SAli Bahrami## ReadInterface(file, alias)
26875ce41a5SAli Bahrami#
26975ce41a5SAli Bahrami# Read the interface description file, as produced by interface_check, and
27075ce41a5SAli Bahrami# return a hash describing it.
27175ce41a5SAli Bahrami#
27275ce41a5SAli Bahrami# entry:
27375ce41a5SAli Bahrami#	file - Interface file to read.
27475ce41a5SAli Bahrami#	alias - Refence to hash to be filled in with any aliases
27575ce41a5SAli Bahrami#		that are seen in the file. The alias name is the key,
27675ce41a5SAli Bahrami#		and the object is the value.
27775ce41a5SAli Bahrami#
27875ce41a5SAli Bahrami# exit:
27975ce41a5SAli Bahrami#	The hash referenced by alias has been updated.
28075ce41a5SAli Bahrami#
28175ce41a5SAli Bahrami#	The return value is a hash that encapsulates the interface
28275ce41a5SAli Bahrami#	information. This hash returned uses the object names as the
28375ce41a5SAli Bahrami#	key. Each key references a sub-hash that contains information
28475ce41a5SAli Bahrami#	for that object:
28575ce41a5SAli Bahrami#
28675ce41a5SAli Bahrami#	CLASS		-> ELFCLASS
28775ce41a5SAli Bahrami#	TYPE		-> ELF type
28875ce41a5SAli Bahrami#	VERSION_NAMES	-> Reference to array [1..n] of version names, in the
28975ce41a5SAli Bahrami#			   order they come from the input file.
29075ce41a5SAli Bahrami#	VERSION_INFO	-> Reference to hash indexed by version name, yielding
29175ce41a5SAli Bahrami#			   a reference to an array containing information about
29275ce41a5SAli Bahrami#			   that version.
29375ce41a5SAli Bahrami#
29475ce41a5SAli Bahrami#	The arrays referenced via VERSION_INFO are of the form:
29575ce41a5SAli Bahrami#
29675ce41a5SAli Bahrami#		(top, new, total, symhashref, inheritarrref)
29775ce41a5SAli Bahrami#
29875ce41a5SAli Bahrami#	where:
29975ce41a5SAli Bahrami#		top - 1 if version is a TOP_VERSION, 0 for a regular VERSION
30075ce41a5SAli Bahrami#		new - Number of symbols defined explicitly by version
30175ce41a5SAli Bahrami#		total - Number of symbols included in version, both new,
30275ce41a5SAli Bahrami#			and via inheritance.
30375ce41a5SAli Bahrami#		symhashref - Reference to hash indexed by symbol names, and
30475ce41a5SAli Bahrami#			yielding true (1).
30575ce41a5SAli Bahrami#		inheritarrref - Reference to array of names of versions
30675ce41a5SAli Bahrami#			inherited by this one.
30775ce41a5SAli Bahrami#
30875ce41a5SAli Bahramisub ReadInterface {
30975ce41a5SAli Bahrami	my ($file, $alias) = @_;
31075ce41a5SAli Bahrami	my %main_hash = ();
31175ce41a5SAli Bahrami	my $Line;
31275ce41a5SAli Bahrami	my $LineNum = 0;
31375ce41a5SAli Bahrami	my $obj_name;
31475ce41a5SAli Bahrami	my $obj_hash;
31575ce41a5SAli Bahrami	my $sym_ok = 0;
31675ce41a5SAli Bahrami	my $cur_version;
31775ce41a5SAli Bahrami
31875ce41a5SAli Bahrami	open(FILE, $file) || die "$Prog: Unable to open: $file";
31975ce41a5SAli Bahrami
32075ce41a5SAli Bahrami	# Until we see an OBJECT line, nothing else is valid. To
32175ce41a5SAli Bahrami	# simplify the error handling, use a simple initial loop to
32275ce41a5SAli Bahrami	# read the file up to that point
32375ce41a5SAli Bahrami	while ($Line = onbld_elfmod::GetLine(\*FILE, \$LineNum)) {
32475ce41a5SAli Bahrami		if ($Line =~ s/^OBJECT\s+//i) {
32575ce41a5SAli Bahrami		    $obj_name = $Line;
32675ce41a5SAli Bahrami		    $main_hash{$obj_name} = {};
32775ce41a5SAli Bahrami		    $obj_hash = $main_hash{$obj_name};
32875ce41a5SAli Bahrami		    last;
32975ce41a5SAli Bahrami		}
33075ce41a5SAli Bahrami		die "$file: OBJECT expected on line $LineNum: $Line\n";
33175ce41a5SAli Bahrami	}
33275ce41a5SAli Bahrami
33375ce41a5SAli Bahrami	# Read the remainder of the file
33475ce41a5SAli Bahrami	while ($Line = onbld_elfmod::GetLine(\*FILE, \$LineNum)) {
33575ce41a5SAli Bahrami		# Items are parsed in order of decreasing frequency
33675ce41a5SAli Bahrami
33775ce41a5SAli Bahrami		if ($Line =~
33875ce41a5SAli Bahrami		    /^SYMBOL\s+([^\s]+)$/i) {
33975ce41a5SAli Bahrami			my $sym = $1;
34075ce41a5SAli Bahrami
34175ce41a5SAli Bahrami			die "$file: SYMBOL not expected on line $LineNum: $Line\n"
34275ce41a5SAli Bahrami			    if !$sym_ok;
34375ce41a5SAli Bahrami
34475ce41a5SAli Bahrami			$cur_version->[1]++;
34575ce41a5SAli Bahrami			$cur_version->[2]++;
34675ce41a5SAli Bahrami			$cur_version->[3]{$sym} = 'NEW';
34775ce41a5SAli Bahrami			next;
34875ce41a5SAli Bahrami		}
34975ce41a5SAli Bahrami
35075ce41a5SAli Bahrami		if ($Line =~ /^((TOP_)?VERSION)\s+([^\s]+)(\s+\{(.*)\})?\s*$/i) {
35175ce41a5SAli Bahrami			my ($top, $name, $inherit) = ($2, $3, $5);
35275ce41a5SAli Bahrami
35375ce41a5SAli Bahrami			$top = defined($top) ? 1 : 0;
35475ce41a5SAli Bahrami
35575ce41a5SAli Bahrami			my @inheritarr = defined($inherit) ?
35675ce41a5SAli Bahrami			    split /[,{\s]+/, $inherit : ();
35775ce41a5SAli Bahrami
35875ce41a5SAli Bahrami			$cur_version = [ $top, 0, 0, {}, \@inheritarr ];
35975ce41a5SAli Bahrami			$obj_hash->{'VERSION_INFO'}{$name} = $cur_version;
36075ce41a5SAli Bahrami
36175ce41a5SAli Bahrami			push @{$obj_hash->{'VERSION_NAMES'}}, $name;
36275ce41a5SAli Bahrami			$sym_ok = 1;
36375ce41a5SAli Bahrami			next;
36475ce41a5SAli Bahrami		}
36575ce41a5SAli Bahrami
36675ce41a5SAli Bahrami		if ($Line =~ /^OBJECT\s+([^\s]+)$/i) {
36775ce41a5SAli Bahrami		    my $prev_obj_hash = $obj_hash;
36875ce41a5SAli Bahrami		    $obj_name = $1;
36975ce41a5SAli Bahrami		    $main_hash{$obj_name} = {};
37075ce41a5SAli Bahrami		    $obj_hash = $main_hash{$obj_name};
37175ce41a5SAli Bahrami
37275ce41a5SAli Bahrami		    # Expand the versions for the object just processed
37375ce41a5SAli Bahrami		    ExpandInheritance($prev_obj_hash);
37475ce41a5SAli Bahrami		    next;
37575ce41a5SAli Bahrami		}
37675ce41a5SAli Bahrami
37775ce41a5SAli Bahrami		if ($Line =~ /^CLASS\s+([^\s]+)$/i) {
37875ce41a5SAli Bahrami			$obj_hash->{'CLASS'} = $1;
37975ce41a5SAli Bahrami			next;
38075ce41a5SAli Bahrami		}
38175ce41a5SAli Bahrami
38275ce41a5SAli Bahrami		if ($Line =~ /^TYPE\s+([^\s]+)$/i) {
38375ce41a5SAli Bahrami			$obj_hash->{'TYPE'} = $1;
38475ce41a5SAli Bahrami			next;
38575ce41a5SAli Bahrami		}
38675ce41a5SAli Bahrami
38775ce41a5SAli Bahrami		if ($Line =~ /^ALIAS\s+([^\s]+)$/i) {
38875ce41a5SAli Bahrami			$$alias{$1} = $obj_name;
38975ce41a5SAli Bahrami			next;
39075ce41a5SAli Bahrami		}
39175ce41a5SAli Bahrami
39275ce41a5SAli Bahrami		die "$file: unrecognized item on line $LineNum: $Line\n";
39375ce41a5SAli Bahrami	}
39475ce41a5SAli Bahrami	close FILE;
39575ce41a5SAli Bahrami
39675ce41a5SAli Bahrami	# Expand the versions for the final object from the file
39775ce41a5SAli Bahrami	ExpandInheritance($obj_hash);
39875ce41a5SAli Bahrami
39975ce41a5SAli Bahrami	return %main_hash;
40075ce41a5SAli Bahrami}
40175ce41a5SAli Bahrami
40275ce41a5SAli Bahrami## PrintInterface(main_hash, alias)
40375ce41a5SAli Bahrami#
40475ce41a5SAli Bahrami# Dump the contents of main_hash and alias to stdout in the same format
40575ce41a5SAli Bahrami# used by interface_check to produce the input interface file. This output
40675ce41a5SAli Bahrami# should diff cleanly against the original (ignoring the header comments).
40775ce41a5SAli Bahrami#
40875ce41a5SAli Bahramisub PrintInterface {
40975ce41a5SAli Bahrami	my ($main_hash, $alias_hash) = @_;
41075ce41a5SAli Bahrami
41175ce41a5SAli Bahrami	foreach my $obj (sort keys %$main_hash) {
41275ce41a5SAli Bahrami		print "OBJECT\t$obj\n";
41375ce41a5SAli Bahrami		print "CLASS\t$main_hash->{$obj}{'CLASS'}\n";
41475ce41a5SAli Bahrami		print "TYPE\t$main_hash->{$obj}{'TYPE'}\n";
41575ce41a5SAli Bahrami
41675ce41a5SAli Bahrami		# This is inefficient, but good enough for debugging
41775ce41a5SAli Bahrami		# Look at all the aliases and print those that belong
41875ce41a5SAli Bahrami		# to this object.
41975ce41a5SAli Bahrami		foreach my $alias (sort keys %$alias_hash) {
42075ce41a5SAli Bahrami			print "ALIAS\t$alias\n"
42175ce41a5SAli Bahrami			    if ($obj eq $alias_hash->{$alias});
42275ce41a5SAli Bahrami		}
42375ce41a5SAli Bahrami
42475ce41a5SAli Bahrami		next if !defined($main_hash->{$obj}{'VERSION_NAMES'});
42575ce41a5SAli Bahrami
42675ce41a5SAli Bahrami		my $num = scalar(@{$main_hash->{$obj}{'VERSION_NAMES'}});
42775ce41a5SAli Bahrami		my $i;
42875ce41a5SAli Bahrami		for ($i = 0; $i < $num; $i++) {
42975ce41a5SAli Bahrami			my $name = $main_hash->{$obj}{'VERSION_NAMES'}[$i];
43075ce41a5SAli Bahrami			my ($top, $direct, $total, $symhash, $inheritarr) =
43175ce41a5SAli Bahrami			    @{$main_hash->{$obj}{'VERSION_INFO'}{$name}};
43275ce41a5SAli Bahrami
43375ce41a5SAli Bahrami			$top = $top ? "TOP_" : '';
43475ce41a5SAli Bahrami
43575ce41a5SAli Bahrami			my $inherit = (scalar(@$inheritarr) > 0) ?
43675ce41a5SAli Bahrami			    "\t{" . join(', ', @{$inheritarr}) . "}" : '';
43775ce41a5SAli Bahrami
43875ce41a5SAli Bahrami			print "${top}VERSION\t$name$inherit\n";
43975ce41a5SAli Bahrami
44075ce41a5SAli Bahrami			foreach my $sym (sort keys %$symhash) {
44175ce41a5SAli Bahrami				print "\t$symhash->{$sym}\t$sym\n";
44275ce41a5SAli Bahrami			}
44375ce41a5SAli Bahrami		}
44475ce41a5SAli Bahrami	}
44575ce41a5SAli Bahrami}
44675ce41a5SAli Bahrami
44775ce41a5SAli Bahrami## compare()
44875ce41a5SAli Bahrami#
44975ce41a5SAli Bahrami# Compare the old interface definition contained in (%old_hash, %old_alias)
45075ce41a5SAli Bahrami# with the new interface contained in (%new_hash, %new_alias).
45175ce41a5SAli Bahrami#
45275ce41a5SAli Bahramisub compare {
45375ce41a5SAli Bahrami	foreach my $old_obj (sort keys %old_hash) {
45475ce41a5SAli Bahrami		my $new_obj = $old_obj;
45575ce41a5SAli Bahrami		my $Ttl = 0;
45675ce41a5SAli Bahrami
45775ce41a5SAli Bahrami		# If the object does not exist in the new interface,
45875ce41a5SAli Bahrami		# then see if there's an alias for it. Failing that,
45975ce41a5SAli Bahrami		# we simply ignore the object.
46075ce41a5SAli Bahrami		if (!defined($new_hash{$new_obj})) {
46175ce41a5SAli Bahrami			next if !defined($new_alias{$new_obj});
46275ce41a5SAli Bahrami			$new_obj = $new_alias{$new_obj};
46375ce41a5SAli Bahrami		}
46475ce41a5SAli Bahrami
46575ce41a5SAli Bahrami		my $old = $old_hash{$old_obj};
46675ce41a5SAli Bahrami		my $new = $new_hash{$new_obj};
46775ce41a5SAli Bahrami
46875ce41a5SAli Bahrami		# Every version in the old object must exist in the new object,
46975ce41a5SAli Bahrami		# and there must be exactly the same symbols in each.
47075ce41a5SAli Bahrami		my $num = scalar(@{$old->{'VERSION_NAMES'}});
47175ce41a5SAli Bahrami		for (my $i = 0; $i < $num; $i++) {
47275ce41a5SAli Bahrami			my $name = $old->{'VERSION_NAMES'}[$i];
47375ce41a5SAli Bahrami
47475ce41a5SAli Bahrami			# New object must have this version
47575ce41a5SAli Bahrami			if (!defined($new->{'VERSION_INFO'}{$name})) {
47675ce41a5SAli Bahrami				onbld_elfmod::OutMsg2(\*STDOUT, \$Ttl, $old_obj,
47775ce41a5SAli Bahrami				    $new_obj, "$name: deleted version");
47875ce41a5SAli Bahrami				next;
47975ce41a5SAli Bahrami			}
48075ce41a5SAli Bahrami
48175ce41a5SAli Bahrami			my ($old_top, $old_direct, $old_total, $old_symhash) =
48275ce41a5SAli Bahrami			    @{$old->{'VERSION_INFO'}{$name}};
48375ce41a5SAli Bahrami			my ($new_top, $new_direct, $new_total, $new_symhash) =
48475ce41a5SAli Bahrami			    @{$new->{'VERSION_INFO'}{$name}};
48575ce41a5SAli Bahrami
48675ce41a5SAli Bahrami			# If this is an empty top version, and the old object
48775ce41a5SAli Bahrami			# has the EMPTY_TOPVERSION exception set, then we
48875ce41a5SAli Bahrami			# skip it as if it were not present.
48975ce41a5SAli Bahrami			next if $old_top && ($old_direct == 0) &&
49075ce41a5SAli Bahrami			    ExTopVer($name, $old_obj);
49175ce41a5SAli Bahrami
49275ce41a5SAli Bahrami			# We check that every symbol in the old object is
49375ce41a5SAli Bahrami			# in the new one to detect deleted symbols. We then
49475ce41a5SAli Bahrami			# check that every symbol in the new object is also
49575ce41a5SAli Bahrami			# in the old object, to find added symbols. If the
49675ce41a5SAli Bahrami			# "deleted" check is clean, and the two objects have
49775ce41a5SAli Bahrami			# the same number of symbols in their versions, then we
49875ce41a5SAli Bahrami			# can skip the "added" test, because we know that
49975ce41a5SAli Bahrami			# there is no room for an addition to have happened.
50075ce41a5SAli Bahrami			# Since most objects satisfy these constraints, we
50175ce41a5SAli Bahrami			# end up doing roughly half the number of comparisons
50275ce41a5SAli Bahrami			# that would otherwise be needed.
50375ce41a5SAli Bahrami			my $check_added_syms =
50475ce41a5SAli Bahrami			    ($old_total == $new_total) ? 0: 1;
50575ce41a5SAli Bahrami
50675ce41a5SAli Bahrami			# Every symbol in the old version must be in the new one
50775ce41a5SAli Bahrami			foreach my $sym (sort keys %$old_symhash) {
50875ce41a5SAli Bahrami				if (!defined($new_symhash->{$sym})) {
50975ce41a5SAli Bahrami					onbld_elfmod::OutMsg2(\*STDOUT,
51075ce41a5SAli Bahrami					   \$Ttl, $old_obj, $new_obj,
51175ce41a5SAli Bahrami					   "$name: deleted interface: $sym")
51275ce41a5SAli Bahrami					    if !ExSym(\@DelSymList,
51375ce41a5SAli Bahrami						      $sym, $name, $new_obj);
51475ce41a5SAli Bahrami					$check_added_syms = 1;
51575ce41a5SAli Bahrami				}
51675ce41a5SAli Bahrami			}
51775ce41a5SAli Bahrami
51875ce41a5SAli Bahrami			# Do the "added" check, unless we can optimize it away.
51975ce41a5SAli Bahrami			# Every symbol in the new version must be in the old one.
52075ce41a5SAli Bahrami			if ($check_added_syms) {
52175ce41a5SAli Bahrami				foreach my $sym (sort keys %$new_symhash) {
52275ce41a5SAli Bahrami				    if (!defined($old_symhash->{$sym})) {
52375ce41a5SAli Bahrami					next if ExSym(\@AddSymList,
52475ce41a5SAli Bahrami					    $sym, $name, $new_obj);
52575ce41a5SAli Bahrami					onbld_elfmod::OutMsg2(\*STDOUT,
52675ce41a5SAli Bahrami					       \$Ttl, $old_obj, $new_obj,
52775ce41a5SAli Bahrami					       "$name: added interface: $sym");
52875ce41a5SAli Bahrami				    }
52975ce41a5SAli Bahrami				}
53075ce41a5SAli Bahrami			}
53175ce41a5SAli Bahrami
53275ce41a5SAli Bahrami			# We want to ensure that version numbers in an
53375ce41a5SAli Bahrami			# inheritance chain don't go up by more than 1 in
53475ce41a5SAli Bahrami			# any given release. If the version names are in the
535*5253169eSAli Bahrami			# numbered <PREFIX>x.y[.z] format, we can compare the
53675ce41a5SAli Bahrami			# two top versions and see if this has happened.
53775ce41a5SAli Bahrami			#
538*5253169eSAli Bahrami			# For a given <PREFIX>x.y[.z], valid sucessors would
539*5253169eSAli Bahrami			# be <PREFIX>x.(y+1) or <PREFIX>x.y.(z+1), where z is
54075ce41a5SAli Bahrami			# assumed to be 0 if not present.
54175ce41a5SAli Bahrami			#
54275ce41a5SAli Bahrami			# This check only makes sense when the new interface
54375ce41a5SAli Bahrami			# is a direct decendent of the old one, as specified
54475ce41a5SAli Bahrami			# via the -d option. If the two interfaces are more
54575ce41a5SAli Bahrami			# than one release apart, we should not do this test.
546*5253169eSAli Bahrami			next if !($opt{d} && $old_top && !$new_top);
547*5253169eSAli Bahrami
548*5253169eSAli Bahrami			# Known numbered version?
549*5253169eSAli Bahrami			#
550*5253169eSAli Bahrami			# Key to @Cat contents:
551*5253169eSAli Bahrami			# [0]   'NUMBERED'
552*5253169eSAli Bahrami			# [1]	number of dot separated numeric fields. 2 or 3.
553*5253169eSAli Bahrami			# [2]   prefix
554*5253169eSAli Bahrami			# [3]   major #
555*5253169eSAli Bahrami			# [4]   minor #
556*5253169eSAli Bahrami			# [5]   micro # (only if [1] is 3)
557*5253169eSAli Bahrami			my @Cat = onbld_elfmod_vertype::Category($name, '');
558*5253169eSAli Bahrami			next if ($Cat[0] ne 'NUMBERED');
559*5253169eSAli Bahrami
560*5253169eSAli Bahrami			my $iname1 = "$Cat[2]$Cat[3]." . ($Cat[4] + 1);
56175ce41a5SAli Bahrami			my $iname2;
562*5253169eSAli Bahrami			if ($Cat[1] == 3) {
563*5253169eSAli Bahrami			    $iname2 = "$Cat[2]$Cat[3].$Cat[4]." . ($Cat[5] + 1);
56475ce41a5SAli Bahrami			} else {
565*5253169eSAli Bahrami			    $iname2 = "$Cat[2]$Cat[3].$Cat[4].1";
56675ce41a5SAli Bahrami			}
56775ce41a5SAli Bahrami
56875ce41a5SAli Bahrami			if (defined($new->{'VERSION_INFO'}{$iname1}) ||
56975ce41a5SAli Bahrami			    defined($new->{'VERSION_INFO'}{$iname2})) {
57075ce41a5SAli Bahrami				my $i_top =
57175ce41a5SAli Bahrami				    $new->{'VERSION_INFO'}{$iname1}[0] ||
57275ce41a5SAli Bahrami				    $new->{'VERSION_INFO'}{$iname2}[0];
57375ce41a5SAli Bahrami				if (!$i_top) {
57475ce41a5SAli Bahrami					onbld_elfmod::OutMsg2(\*STDOUT,
57575ce41a5SAli Bahrami					    \$Ttl, $old_obj, $new_obj,
57675ce41a5SAli Bahrami					    "$name: inconsistant " .
57775ce41a5SAli Bahrami					    "version increment: " .
57875ce41a5SAli Bahrami					    "expect $iname1 or $iname2 ".
57975ce41a5SAli Bahrami					    "to replace top version");
58075ce41a5SAli Bahrami				}
58175ce41a5SAli Bahrami			} else {
58275ce41a5SAli Bahrami 				onbld_elfmod::OutMsg2(\*STDOUT,
58375ce41a5SAli Bahrami				    \$Ttl, $old_obj, $new_obj,
58475ce41a5SAli Bahrami			            "$name: expected superseding " .
58575ce41a5SAli Bahrami				    "top version to $name not " .
58675ce41a5SAli Bahrami				    "present: $iname1 or $iname2");
58775ce41a5SAli Bahrami			}
58875ce41a5SAli Bahrami		}
58975ce41a5SAli Bahrami
59075ce41a5SAli Bahrami
59175ce41a5SAli Bahrami		# Empty versions in the established interface description
59275ce41a5SAli Bahrami		# are usually the result of fixing a versioning mistake
59375ce41a5SAli Bahrami		# at some point in the past. These versions are part of
59475ce41a5SAli Bahrami		# the public record, and cannot be changed now. However, if
59575ce41a5SAli Bahrami		# comparing two interface descriptions from the same gate,
59675ce41a5SAli Bahrami		# flag any empty versions in the new interface description
59775ce41a5SAli Bahrami		# that are not present in the old one. These have yet to
59875ce41a5SAli Bahrami		# become part of the official interface, and should be removed
59975ce41a5SAli Bahrami		# before they do.
60075ce41a5SAli Bahrami		next if !$opt{d};
60175ce41a5SAli Bahrami
60275ce41a5SAli Bahrami		$num = scalar(@{$new->{'VERSION_NAMES'}});
60375ce41a5SAli Bahrami		for (my $i = 0; $i < $num; $i++) {
60475ce41a5SAli Bahrami			my $name = $new->{'VERSION_NAMES'}[$i];
60575ce41a5SAli Bahrami
60675ce41a5SAli Bahrami			# If old object has this version, skip it
60775ce41a5SAli Bahrami			next if defined($old->{'VERSION_INFO'}{$name});
60875ce41a5SAli Bahrami
60975ce41a5SAli Bahrami			# If explicitly whitelisted, skip it
61075ce41a5SAli Bahrami			next if ExTopVer($name, $new_obj);
61175ce41a5SAli Bahrami
61275ce41a5SAli Bahrami			my ($new_top, $new_direct, $new_total, $new_symhash) =
61375ce41a5SAli Bahrami			    @{$new->{'VERSION_INFO'}{$name}};
61475ce41a5SAli Bahrami
61575ce41a5SAli Bahrami			if ($new_direct == 0) {
61675ce41a5SAli Bahrami				onbld_elfmod::OutMsg2(\*STDOUT,
61775ce41a5SAli Bahrami				    \$Ttl, $old_obj, $new_obj,
61875ce41a5SAli Bahrami				    "$name: invalid empty new version");
61975ce41a5SAli Bahrami			}
62075ce41a5SAli Bahrami		}
62175ce41a5SAli Bahrami	}
62275ce41a5SAli Bahrami
62375ce41a5SAli Bahrami}
62475ce41a5SAli Bahrami
62575ce41a5SAli Bahrami
62675ce41a5SAli Bahrami
62775ce41a5SAli Bahrami# -----------------------------------------------------------------------------
62875ce41a5SAli Bahrami
62975ce41a5SAli Bahrami# Establish a program name for any error diagnostics.
63075ce41a5SAli Bahramichomp($Prog = `basename $0`);
63175ce41a5SAli Bahrami
63275ce41a5SAli Bahrami# Check that we have arguments. Normally, 2 plain arguments are required,
63375ce41a5SAli Bahrami# but if -t is present, only one is allowed.
634*5253169eSAli Bahramiif ((getopts('c:de:ot', \%opt) == 0) || (scalar(@ARGV) != ($opt{t} ? 1 : 2))) {
635*5253169eSAli Bahrami	print "usage: $Prog [-dot] [-c vtype_mod] [-e exfile] old new\n";
636*5253169eSAli Bahrami	print "\t[-c vtype_mod]\tsupply alternative version category module\n";
63775ce41a5SAli Bahrami	print "\t[-d]\t\tnew is a direct decendent of old\n";
63875ce41a5SAli Bahrami	print "\t[-e exfile]\texceptions file\n";
63975ce41a5SAli Bahrami	print "\t[-o]\t\tproduce one-liner output (prefixed with pathname)\n";
64075ce41a5SAli Bahrami	print "\t[-t]\tParse old, and recreate to stdout\n";
64175ce41a5SAli Bahrami	exit 1;
64275ce41a5SAli Bahrami}
64375ce41a5SAli Bahrami
644*5253169eSAli Bahrami# We depend on the onbld_elfmod and onbld_elfmod_vertype perl modules.
645*5253169eSAli Bahrami# Both modules are maintained in the same directory as this script,
646*5253169eSAli Bahrami# and are installed in ../lib/perl. Use the local one if present,
647*5253169eSAli Bahrami# and the installed one otherwise.
648*5253169eSAli Bahrami#
649*5253169eSAli Bahrami# The caller is allowed to supply an alternative implementation for
650*5253169eSAli Bahrami# onbld_elfmod_vertype via the -c option. In this case, the alternative
651*5253169eSAli Bahrami# implementation is expected to provide the same interface as the standard
652*5253169eSAli Bahrami# copy, and is loaded instead.
653*5253169eSAli Bahrami#
654*5253169eSAli Bahramimy $moddir = my $vermoddir = dirname($0);
655*5253169eSAli Bahrami$moddir = "$moddir/../lib/perl" if ! -f "$moddir/onbld_elfmod.pm";
656*5253169eSAli Bahramirequire "$moddir/onbld_elfmod.pm";
657*5253169eSAli Bahramiif ($opt{c}) {
658*5253169eSAli Bahrami	require "$opt{c}";
659*5253169eSAli Bahrami} else {
660*5253169eSAli Bahrami	$vermoddir = "$vermoddir/../lib/perl"
661*5253169eSAli Bahrami	    if ! -f "$vermoddir/onbld_elfmod_vertype.pm";
662*5253169eSAli Bahrami	require "$vermoddir/onbld_elfmod_vertype.pm";
663*5253169eSAli Bahrami}
664*5253169eSAli Bahrami
66575ce41a5SAli Bahrami# Locate and process the exceptions file
66675ce41a5SAli BahramiLoadExceptions();
66775ce41a5SAli Bahrami
66875ce41a5SAli Bahrami%old_alias = ();
66975ce41a5SAli Bahrami%old_hash = ReadInterface($ARGV[0], \%old_alias);
67075ce41a5SAli Bahrami
67175ce41a5SAli Bahrami# If -t is present, only one argument is allowed --- we parse it, and then
67275ce41a5SAli Bahrami# print the same information back to stderr in the same format as the original.
67375ce41a5SAli Bahrami# This is useful for debugging, to verify that the parsing is correct.
67475ce41a5SAli Bahramiif ($opt{t}) {
67575ce41a5SAli Bahrami	PrintInterface(\%old_hash, \%old_alias);
67675ce41a5SAli Bahrami	exit 0;
67775ce41a5SAli Bahrami}
67875ce41a5SAli Bahrami
67975ce41a5SAli Bahrami%new_alias = ();
68075ce41a5SAli Bahrami%new_hash = ReadInterface($ARGV[1], \%new_alias);
68175ce41a5SAli Bahrami
68275ce41a5SAli Bahramicompare();
68375ce41a5SAli Bahrami
68475ce41a5SAli Bahramiexit 0;
685