meta/structs.pl (143 lines of code) (raw):
#!/usr/bin/perl
#
# Copyright (c) 2021 Microsoft Open Technologies, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT
# LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS
# FOR A PARTICULAR PURPOSE, MERCHANTABILITY OR NON-INFRINGEMENT.
#
# See the Apache Version 2.0 License for specific language governing
# permissions and limitations under the License.
#
# Microsoft would like to thank the following companies for their review and
# assistance with these files: Intel Corporation, Mellanox Technologies Ltd,
# Dell Products, L.P., Facebook, Inc., Marvell International Ltd.
#
# @file structs.pl
#
# @brief This module defines structs backward compatibility check for SAI headers
#
BEGIN { push @INC,'.'; }
use strict;
use warnings;
use diagnostics;
use sort 'stable'; # for sort
use Getopt::Std;
use Data::Dumper;
use utils;
my %options = ();
getopts("dsASlDH:", \%options);
our $optionPrintDebug = 1 if defined $options{d};
our $optionDisableAspell = 1 if defined $options{A};
our $optionUseXmlSimple = 1 if defined $options{s};
our $optionDisableStyleCheck = 1 if defined $options{S};
our $optionShowLogCaller = 1 if defined $options{l};
our $optionDumpHistoryFile = 1 if defined $options{D};
our $optionHistoryFile = $options{H} if defined $options{H};
$SIG{__DIE__} = sub
{
LogError "FATAL ERROR === MUST FIX === : @_";
exit 1;
};
our $INCLUDE_DIR = "temp";
our %SAI_STRUCTS = ();
our %HISTORY = ();
# TODO: union should be treated as hash map, since order don't matter
sub ProcessSingleHeader
{
my $header = shift;
my $data = ReadHeaderFile $header;
my @lines = split/\n/,$data;
my $currentStruct = "undefined";
for my $line (@lines)
{
if ($line =~ /^\s*typedef\s+(struct|union)\s+_((sai_\w+_)t)/)
{
$currentStruct = $2;
my @fields = ();
$SAI_STRUCTS{$currentStruct}->{fields} = \@fields;
$SAI_STRUCTS{$currentStruct}->{type} = $1;
LogDebug "found $1 $currentStruct";
next;
}
$currentStruct = "undefined" if $line =~ /}\s*$currentStruct/;
next if $currentStruct eq "undefined";
next if $line =~ /^{?$/; # skip struct open bracket and empty lines
next if $line =~ m!^\s*/?\*!; # skip comment
if ($line =~ /^\s*((\w+)\s+\*?(\w+)(\[\d+\])?);/)
{
my $field = $1;
$field =~ s/\s+/ /g;
# NOTE: field type is as string here, is of actual type is complex,
# like for example function pointer and parameters of that function
# will change, then this check will not catch that, we will need
# then a much more complex type checking, it could be added
push @{ $SAI_STRUCTS{$currentStruct}->{fields} }, $field;
}
else
{
LogError "unknown struct/union line '$line', FIXME";
}
}
}
sub ProcessHeaders
{
my $commit = shift;
my @headers = GetHeaderFiles("temp/commit-$commit/inc");
for my $header (@headers)
{
LogDebug "Processing $header";
ProcessSingleHeader "temp/commit-$commit/inc/$header";
}
}
sub BuildCommitHistory
{
my $commit = shift;
for my $structTypeName (sort keys %SAI_STRUCTS)
{
LogDebug $structTypeName;
if ($structTypeName eq "sai_object_key_entry_t")
{
# skip this union, since it contain experimental entries
# and it can be modified time to time
next;
}
my $arr_ref = $SAI_STRUCTS{$structTypeName}->{fields};
my $type = $SAI_STRUCTS{$structTypeName}->{type};
if (not defined $HISTORY{$structTypeName})
{
$HISTORY{$structTypeName}{values} = $arr_ref;
next;
}
LogDebug "compare $structTypeName on $commit";
my $hist_arr_ref = $HISTORY{$structTypeName}{values};
my $currCount = scalar @$arr_ref;
my $histCount = scalar @$hist_arr_ref;
LogDebug "histCount $histCount vs currCount $currCount on $structTypeName";
# NOTE: we allow api structs to change member count since we can add new api
# at the end of the struct.
#
# NOTE: we also allow to change number of members in union, since size
# of union may not increase by adding members, and actual union size
# check is performed by sai sanity check
if ($currCount != $histCount and not $structTypeName =~ /^sai_\w+_api_t$/
and $structTypeName ne "sai_switch_health_data_t"
and $structTypeName ne "sai_port_oper_status_notification_t")
{
LogError "FATAL: struct $structTypeName member count differs, was $histCount but is $currCount on commit $commit" if $type eq "struct";
}
if ($histCount > $currCount)
{
LogError "FATAL: $structTypeName members were removed on commit $commit, NOT ALLOWED!";
exit 1;
}
my $minCount = ($histCount > $currCount) ? $currCount : $histCount;
for (my $idx = 0; $idx < $minCount; $idx++)
{
my $hist_field = $hist_arr_ref->[$idx];
my $field = $arr_ref->[$idx];
next if $hist_field eq $field;
LogError "FATAL: field on index $idx do not match, was '$hist_field' and now is '$field' on $commit";
}
if ($histCount != $currCount)
{
LogInfo "updating $structTypeName since member count changed from $histCount to $currCount";
$HISTORY{$structTypeName}{values} = $arr_ref;
}
# NOTE: we could allow some other structs than *_api_t to also be
# extended in th future (added fields at the end), for example simple
# structures that are used as attribute value, and not lists
}
}
sub CleanData
{
%SAI_STRUCTS = ();
}
#
# MAIN
#
if (defined $optionHistoryFile)
{
my $history = ReadHeaderFile($optionHistoryFile);
eval($history) or die "failed to eval history file: $optionHistoryFile";
die "history file $optionHistoryFile not complete, missing too many keys" if scalar keys %HISTORY < 133;
LogInfo "loaded history from $optionHistoryFile";
}
for my $commit (@ARGV)
{
# reset
LogInfo "processing commit $commit";
CleanData();
ProcessHeaders $commit;
#print Dumper \%SAI_STRUCTS;
BuildCommitHistory $commit;
}
ExitOnErrorsOrWarnings();
if (defined $optionDumpHistoryFile and (scalar @ARGV > 0))
{
$Data::Dumper::Indent = 0;
my $history = Data::Dumper->Dump([\%HISTORY],[qw/*HISTORY/]);
my $lastCommit = $ARGV[-1];
WriteFile("structs.$lastCommit.history", $history);
LogInfo "ancestry history file saved to: structs.$lastCommit.history";
}