#!/usr/bin/env perl require 5.003; use warnings; # Global settings # (The max_idx parameter is the only thing that needs to be changed when adding # support for a new major release. If support for a prior major release # is added (like support for 1.4, etc), the min_sup_idx parameter will # need to be decremented.) # Max. library "index" (0 = v1.0, 1 = 1.2, 2 = 1.4, 3 = 1.6, 4 = 1.8, 5 = 1.10, 6 = 1.12, 7 = 1.14, etc) $max_idx = 7; # Min. supported previous library version "index" (0 = v1.0, 1 = 1.2, etc) $min_sup_idx = 3; # Number of spaces to indent preprocessor commands inside ifdefs $indent = 2; # # Copyright by The HDF Group. # All rights reserved. # # This file is part of HDF5. The full HDF5 copyright notice, including # terms governing use, modification, and redistribution, is contained in # the COPYING file, which can be found at the root of the source code # distribution tree, or in https://www.hdfgroup.org/licenses. # If you do not have access to either file, you may request a copy from # help@hdfgroup.org. # # Create public symbol version headers # # Read in the public symbol version description text file and create the # appropriate headers needed by the library. # ############################################################################## # Print the copyright into an open file # sub print_copyright ($) { my $fh = shift; print $fh "/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n"; print $fh " * Copyright by The HDF Group. *\n"; print $fh " * All rights reserved. *\n"; print $fh " * *\n"; print $fh " * This file is part of HDF5. The full HDF5 copyright notice, including *\n"; print $fh " * terms governing use, modification, and redistribution, is contained in *\n"; print $fh " * the COPYING file, which can be found at the root of the source code *\n"; print $fh " * distribution tree, or in https://www.hdfgroup.org/licenses. *\n"; print $fh " * If you do not have access to either file, you may request a copy from *\n"; print $fh " * help\@hdfgroup.org. *\n"; print $fh " * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */\n"; } ############################################################################## # Print the "do not change this file" warning # sub print_warning ($) { my $fh = shift; print $fh "\n/* Generated automatically by bin/make_vers -- do not edit */\n"; print $fh "/* Add new versioned symbols to H5vers.txt file */\n\n"; } ############################################################################## # Print start of ifdef's to prevent a file from being re-included # sub print_startprotect ($$) { my ($fh, $file) = @_; # Clip off the ".h" part of the name $file =~ s/(\w*)\.h/$1/; # Print the ifdef info print $fh "\n#ifndef ${file}_H\n"; print $fh "#define ${file}_H\n"; } ############################################################################## # Print check for conflicting version macro settings # sub print_checkoptions ($) { my $fh = shift; # File handle for output file my $curr_idx; # Current API version index # Print the option checking print $fh "\n\n/* Issue error if contradicting macros have been defined. */\n"; print $fh "/* (Can't use an older (deprecated) API version if deprecated symbols have been disabled) */\n"; # Print the #ifdef print $fh "#if ("; for $curr_idx ($min_sup_idx .. ($max_idx - 1)) { print $fh "defined(H5_USE_1", ($curr_idx * 2), "_API)"; if($curr_idx < ($max_idx - 1)) { print $fh " || "; } } print $fh ") && defined(H5_NO_DEPRECATED_SYMBOLS)\n"; # Print the error for bad API version chosen print $fh ' ' x $indent, "#error \"Can't choose old API versions when deprecated APIs are disabled\"\n"; # Print the #endif print $fh "#endif /* ("; for $curr_idx ($min_sup_idx .. ($max_idx - 1)) { print $fh "defined(H5_USE_1", ($curr_idx * 2), "_API)"; if($curr_idx < ($max_idx - 1)) { print $fh " || "; } } print $fh ") && defined(H5_NO_DEPRECATED_SYMBOLS) */\n"; } ############################################################################## # Print "global" API version macro settings # sub print_globalapidefvers ($) { my $fh = shift; # File handle for output file my $curr_idx; # Current API version index # Print the descriptive comment print $fh "\n\n/* If a particular default \"global\" version of the library's interfaces is\n"; print $fh " * chosen, set the corresponding version macro for API symbols.\n"; print $fh " *\n"; print $fh " */\n"; for $curr_idx ($min_sup_idx .. ($max_idx - 1)) { # Print API version ifdef print $fh "\n#if defined(H5_USE_1", ($curr_idx * 2), "_API_DEFAULT) && !defined(H5_USE_1", ($curr_idx * 2), "_API)\n"; # Print API version definition print $fh " " x $indent, "#define H5_USE_1", ($curr_idx * 2), "_API 1\n"; # Print API version endif print $fh "#endif /* H5_USE_1", ($curr_idx * 2), "_API_DEFAULT && !H5_USE_1", ($curr_idx * 2), "_API */\n"; } } ############################################################################## # Print "global" API symbol version macro settings # sub print_globalapisymbolvers ($) { my $fh = shift; # File handle for output file my $curr_idx; # Current API version index # Print the descriptive comment print $fh "\n\n/* If a particular \"global\" version of the library's interfaces is chosen,\n"; print $fh " * set the versions for the API symbols affected.\n"; print $fh " *\n"; print $fh " * Note: If an application has already chosen a particular version for an\n"; print $fh " * API symbol, the individual API version macro takes priority.\n"; print $fh " */\n"; # Loop over supported older library APIs and define the appropriate macros for $curr_idx ($min_sup_idx .. ($max_idx - 1)) { # Print API version ifdef print $fh "\n#ifdef H5_USE_1", ($curr_idx * 2), "_API\n"; # Print the version macro info for each function that is defined for # this API version print $fh "\n/*************/\n"; print $fh "/* Functions */\n"; print $fh "/*************/\n"; for $name (sort keys %{$func_vers[$curr_idx]}) { print $fh "\n#if !defined(", $name, "_vers)\n"; print $fh " " x $indent, "#define ", $name, "_vers $func_vers[$curr_idx]{$name}\n"; print $fh "#endif /* !defined(", $name, "_vers) */\n"; } # Print the version macro info for each typedef that is defined for # this API version print $fh "\n/************/\n"; print $fh "/* Typedefs */\n"; print $fh "/************/\n"; for $name (sort keys %{$type_vers[$curr_idx]}) { print $fh "\n#if !defined(", $name, "_t_vers)\n"; print $fh " " x $indent, "#define ", $name, "_t_vers $type_vers[$curr_idx]{$name}\n"; print $fh "#endif /* !defined(", $name, "_t_vers) */\n"; } # Print API version endif print $fh "\n#endif /* H5_USE_1", ($curr_idx * 2), "_API */\n"; } } ############################################################################## # Print "default" API version macro settings # sub print_defaultapivers ($) { my $fh = shift; # File handle for output file my $curr_name; # Current API function # Print the descriptive comment print $fh "\n\n/* Choose the correct version of each API symbol, defaulting to the latest\n"; print $fh " * version of each. The \"best\" name for API parameters/data structures\n"; print $fh " * that have changed definitions is also set. An error is issued for\n"; print $fh " * specifying an invalid API version.\n"; print $fh " */\n"; # Loop over function names that are versioned and set up the version macros print $fh "\n/*************/\n"; print $fh "/* Functions */\n"; print $fh "/*************/\n"; for $curr_name (sort keys %functions) { my $curr_vers_name; # Name of version macro for current function my $curr_vers; # Version of function my @param_list; # Typedefs for the function parameters # Set up variables for later use $curr_vers_name = $curr_name . "_vers"; $curr_vers = $functions{$curr_name}; # Split up parameter info @param_list = split(/\s*,\s*/, $func_params{$curr_name}); #print "print_defaultapivers: param_list=(@param_list)\n"; # Set up default/latest version name mapping print $fh "\n#if !defined($curr_vers_name) || $curr_vers_name == $curr_vers\n"; print $fh " " x $indent, "#ifndef $curr_vers_name\n"; print $fh " " x ($indent * 2), "#define $curr_vers_name $curr_vers\n"; print $fh " " x $indent, "#endif /* $curr_vers_name */\n"; print $fh " " x $indent, "#define $curr_name $curr_name$curr_vers\n"; # Print function's dependent parameter types foreach(sort(@param_list)) { print $fh " " x $indent, "#define ${_}_t $_${curr_vers}_t\n"; } # Loop to print earlier version name mappings $curr_vers--; while($curr_vers > 0) { print $fh "#elif $curr_vers_name == $curr_vers\n"; print $fh " " x $indent, "#define $curr_name $curr_name$curr_vers\n"; # Print function's dependent parameter types foreach(sort(@param_list)) { print $fh " " x $indent, "#define ${_}_t $_${curr_vers}_t\n"; } $curr_vers--; } # Finish up with error for unknown version and endif print $fh "#else /* $curr_vers_name */\n"; print $fh " " x $indent, "#error \"$curr_vers_name set to invalid value\"\n"; print $fh "#endif /* $curr_vers_name */\n"; } # Loop over typedefs that are versioned and set up the version macros print $fh "\n/************/\n"; print $fh "/* Typedefs */\n"; print $fh "/************/\n"; for $curr_name (sort keys %typedefs) { my $curr_vers_name; # Name of version macro for current function my $curr_vers; # Version of function # Set up variables for later use $curr_vers_name = $curr_name . "_t_vers"; $curr_vers = $typedefs{$curr_name}; # Set up default/latest version name mapping print $fh "\n#if !defined($curr_vers_name) || $curr_vers_name == $curr_vers\n"; print $fh " " x $indent, "#ifndef $curr_vers_name\n"; print $fh " " x ($indent * 2), "#define $curr_vers_name $curr_vers\n"; print $fh " " x $indent, "#endif /* $curr_vers_name */\n"; print $fh " " x $indent, "#define ${curr_name}_t $curr_name${curr_vers}_t\n"; # Loop to print earlier version name mappings $curr_vers--; while($curr_vers > 0) { print $fh "#elif $curr_vers_name == $curr_vers\n"; print $fh " " x $indent, "#define ${curr_name}_t $curr_name${curr_vers}_t\n"; $curr_vers--; } # Finish up with error for unknown version and endif print $fh "#else /* $curr_vers_name */\n"; print $fh " " x $indent, "#error \"$curr_vers_name set to invalid value\"\n"; print $fh "#endif /* $curr_vers_name */\n\n"; } } ############################################################################## # Print end of ifdef's to prevent a file from being re-included # sub print_endprotect ($$) { my ($fh, $file) = @_; # Clip off the ".h" part of the name $file =~ s/(\w*)\.h/$1/; # Print the endif info print $fh "#endif /* ${file}_H */\n\n"; } ############################################################################## # Parse a meaningful line (not a comment or blank line) into the appropriate # data structure # sub parse_line ($) { my $line = shift; # Get the line to parse # Parse API function lines #print "line=$line\n"; if($line =~ /^\s*FUNCTION:/ || $line =~ /^\s*TYPEDEF:/) { my $name; # The name of the function my $params; # Typedefs for function parameters my $vers; # The version info for the function my @vers_list; # Version info, as a list my @vers_nums; # Version info, as a numeric list my $num_versions; # Number of versions for function my %sym_versions; # Versions for a symbol my $last_idx; # The previous version index seen for a function my $last_vers; # The previous version # seen for a function my $line_type; # Type of line we are parsing # Determine the type of the line to parse if($line =~ /^\s*FUNCTION:/) { $line_type = 1; # Get the function's name & version info ($name, $params, $vers) = ($line =~ /^\s*FUNCTION:\s*(\w*);\s*(.*?)\s*;\s*(.*?)\s*$/); #print "parse_line: name='$name', params='$params', vers='$vers'\n"; } elsif($line =~ /^\s*TYPEDEF:/) { $line_type = 2; # Get the typedefs's name & version info ($name, $vers) = ($line =~ /^\s*TYPEDEF:\s*(\w*);\s*(.*?)\s*$/); #print "parse_line: name='$name', vers='$vers'\n"; } #print "parse_line: line_type='$line_type'\n"; # Check if the name already exists in the list of symbols if(exists($functions{$name}) || exists($typedefs{$name})) { die "duplicated symbol: $name"; } # Check for no version info given if($vers eq "") { die "no version information: $name"; } # Split up version info @vers_list = split(/\s*,\s*/, $vers); #print "parse_line: vers_list=(@vers_list)\n"; # Parse the version list into numbers, checking for invalid input foreach(@vers_list) { my $vers_idx; # Index of version in array # Do some validation on the input # Note: v111 is allowed because H5O functions were prematurely versioned # in HDF5 1.10. Because users were affected by this, the versioning # was rescinded but the H5O version 2 functions were kept to be # called directly. Now that the version macros are added in 1.12, # along with a 3rd version of the H5O functions, the H5O function # version for default api=v110 should be version 1 to work correctly # with 1.10 applications that were using unversioned H5O functions, # and the H5O function version should be version 3 for default api=v112 # (the default api version for 1.12). Allowing a v111 entry and # incrementing its index 13 lines below allows a version 2 that is # never accessed via the H5O function macros. if(!( $_ =~ /v1[02468]/ || $_ =~ /v11[02468]/ || $_ =~ /v111/ )) { die "bad version information: $name"; } if(exists($sym_versions{$_})) { die "duplicate version information: $name"; } # Store the versions for the function in a local hash table, indexed by the version $sym_versions{$_}=$_; #print "parse_line: _=$_\n"; # Get the index of the version ($vers_idx) = ($_ =~ /v1(\d+)/); if($vers_idx == 11) { $vers_idx++; } $vers_idx /= 2; #print "parse_line: vers_idx='$vers_idx'\n"; push(@vers_nums, $vers_idx); } #print "parse_line: vers_nums=(@vers_nums)\n"; # Check for invalid version info given $last_idx = -1; $last_vers = 1; foreach(sort(@vers_nums)) { #print "parse_line: _=$_ last_idx='$last_idx'\n"; # Update intermediate versions of the library that included the API routine if($last_idx >= 0) { #print "parse_line: name='$name'\n"; #print "parse_line: last_vers='$last_vers'\n"; #print "parse_line: last_idx='$last_idx'\n"; # Add the function to the list of API routines available in # different versions of the library while($last_idx <= $_) { if($line_type == 1) { $func_vers[$last_idx]{$name} = $last_vers; } elsif($line_type == 2) { $type_vers[$last_idx]{$name} = $last_vers; } else { die "unknown line type: $line"; } $last_idx++; } # Increment the version # of the function $last_vers++; } # Keep track of last version index seen $last_idx = $_; } # Finish updating versions of the library that included the API routine if($last_idx >= 0) { #print "parse_line: max_idx='$max_idx'\n"; # Add the function to the list of API routines available in # different versions of the library while($last_idx <= $max_idx) { if($line_type == 1) { $func_vers[$last_idx]{$name} = $last_vers; } elsif($line_type == 2) { $type_vers[$last_idx]{$name} = $last_vers; } else { die "unknown line type: $line"; } $last_idx++; } } # Store the number of symbol versions in a hash table, indexed by the name if($line_type == 1) { $functions{$name} = $#vers_list + 1; # Store the function's parameter types for later $func_params{$name} = $params; } elsif($line_type == 2) { $typedefs{$name} = $#vers_list + 1; } else { die "unknown line type: $line"; } } # Unknown keyword else { die "unknown keyword: $line"; } } ############################################################################## # Create the generated portion of the public header file # sub create_public ($) { my $prefix = shift; # Get the prefix for the generated file my $file = "H5version.h"; # Name of file to generate my $name; # Name of function # Rename previous file # rename "${prefix}${file}", "${prefix}${file}~" or die "unable to make backup"; # Open new header file open HEADER, ">${prefix}${file}" or die "unable to modify source"; # Create file contents print_copyright(*HEADER); print_warning(*HEADER); print_startprotect(*HEADER, $file); print_globalapidefvers(*HEADER); print_checkoptions(*HEADER); print_globalapisymbolvers(*HEADER); print_defaultapivers(*HEADER); print_endprotect(*HEADER, $file); # Close header file close HEADER; } ############################################################################## # Read symbol version file (given as command-line argument) in and process it # into internal data structures, then create header files. # for $file (@ARGV) { my $prefix; # Local prefix for generated files #print "file = '$file'\n"; # Check for directory prefix on input file if($file =~ /\//) { ($prefix) = ($file =~ /(^.*\/)/); } else { $prefix = ""; } #print "prefix = '$prefix'\n"; # Read in the entire file open SOURCE, $file or die "$file: $!\n"; while ( defined ($line=) ) { # Skip blank lines and those lines whose first character is a '#' if(!($line =~ /(^\s*#.*$)|(^\s*$)/)) { # Construct data structures for later printing parse_line($line); } } close SOURCE; # Create header files print "Generating 'H5version.h'\n"; create_public($prefix); #for $name (sort keys %functions) { # print "functions{$name} = $functions{$name}\n"; #} #for $i (0 .. $#func_vers) { # my $vers_name; # Name of indexed version # $vers_name = "v1." . ($i * 2); # print "$vers_name functions: "; # for $name (sort keys %{$func_vers[$i]}) { # print "$name$func_vers[$i]{$name} "; # } # print "\n"; #} }