| #!/usr/bin/env perl |
| |
| # |
| #//===----------------------------------------------------------------------===// |
| #// |
| #// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| #// See https://llvm.org/LICENSE.txt for license information. |
| #// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| #// |
| #//===----------------------------------------------------------------------===// |
| # |
| |
| # Some pragmas. |
| use strict; # Restrict unsafe constructs. |
| use warnings; # Enable all warnings. |
| |
| use FindBin; |
| use lib "$FindBin::Bin/lib"; |
| |
| use tools; |
| |
| our $VERSION = "0.004"; |
| |
| # |
| # Subroutines. |
| # |
| |
| sub parse_input($\%) { |
| |
| my ( $input, $defs ) = @_; |
| my @bulk = read_file( $input ); |
| my %entries; |
| my %ordinals; |
| my @dirs; |
| my $value = 1; |
| |
| my $error = |
| sub { |
| my ( $msg, $l, $line ) = @_; |
| runtime_error( |
| "Error parsing file \"$input\" line $l:\n" . |
| " $line" . |
| ( $msg ? $msg . "\n" : () ) |
| ); |
| }; # sub |
| |
| my $n = 0; # Line number. |
| foreach my $line ( @bulk ) { |
| ++ $n; |
| if ( 0 ) { |
| } elsif ( $line =~ m{^\s*(?:#|\n)} ) { |
| # Empty line or comment. Skip it. |
| } elsif ( $line =~ m{^\s*%} ) { |
| # A directive. |
| if ( 0 ) { |
| } elsif ( $line =~ m{^\s*%\s*if(n)?def\s+([A-Za-z0-9_]+)\s*(?:#|\n)} ) { |
| my ( $negation, $name ) = ( $1, $2 ); |
| my $dir = { n => $n, line => $line, name => $name, value => $value }; |
| push( @dirs, $dir ); |
| $value = ( $value and ( $negation xor $defs->{ $name } ) ); |
| } elsif ( $line =~ m{^\s*%\s*endif\s*(?:#|\n)} ) { |
| if ( not @dirs ) { |
| $error->( "Orphan %endif directive.", $n, $line ); |
| }; # if |
| my $dir = pop( @dirs ); |
| $value = $dir->{ value }; |
| } else { |
| $error->( "Bad directive.", $n, $line ); |
| }; # if |
| } elsif ( $line =~ m{^\s*(-)?\s*([A-Za-z0-9_]+)(?:\s+(\d+|DATA))?\s*(?:#|\n)} ) { |
| my ( $obsolete, $entry, $ordinal ) = ( $1, $2, $3 ); |
| if ( $value ) { |
| if ( exists( $entries{ $entry } ) ) { |
| $error->( "Entry \"$entry\" has already been specified.", $n, $line ); |
| }; # if |
| $entries{ $entry } = { ordinal => $ordinal, obsolete => defined( $obsolete ) }; |
| if ( defined( $ordinal ) and $ordinal ne "DATA" ) { |
| if ( $ordinal >= 1000 and $entry =~ m{\A[ok]mp_} ) { |
| $error->( "Ordinal of user-callable entry must be < 1000", $n, $line ); |
| }; # if |
| if ( $ordinal >= 1000 and $ordinal < 2000 ) { |
| $error->( "Ordinals between 1000 and 1999 are reserved.", $n, $line ); |
| }; # if |
| if ( exists( $ordinals{ $ordinal } ) ) { |
| $error->( "Ordinal $ordinal has already been used.", $n, $line ); |
| }; # if |
| $ordinals{ $ordinal } = $entry; |
| }; # if |
| }; # if |
| } else { |
| $error->( "", $n, $line ); |
| }; # if |
| }; # foreach |
| |
| if ( @dirs ) { |
| my $dir = pop( @dirs ); |
| $error->( "Unterminated %if direcive.", $dir->{ n }, $dir->{ line } ); |
| }; # while |
| |
| return %entries; |
| |
| }; # sub parse_input |
| |
| sub process(\%) { |
| |
| my ( $entries ) = @_; |
| |
| foreach my $entry ( keys( %$entries ) ) { |
| if ( not $entries->{ $entry }->{ obsolete } ) { |
| my $ordinal = $entries->{ $entry }->{ ordinal }; |
| # omp_alloc and omp_free are C/C++ only functions, skip "1000+ordinal" for them |
| if ( $entry =~ m{\A[ok]mp_} and $entry ne "omp_alloc" and $entry ne "omp_free" ) { |
| if ( not defined( $ordinal ) ) { |
| runtime_error( |
| "Bad entry \"$entry\": ordinal number is not specified." |
| ); |
| }; # if |
| if ( $ordinal ne "DATA" ) { |
| $entries->{ uc( $entry ) } = { ordinal => 1000 + $ordinal }; |
| } |
| }; # if |
| }; # if |
| }; # foreach |
| |
| return %$entries; |
| |
| }; # sub process |
| |
| sub generate_output(\%$) { |
| |
| my ( $entries, $output ) = @_; |
| my $bulk; |
| |
| $bulk = "EXPORTS\n"; |
| foreach my $entry ( sort( keys( %$entries ) ) ) { |
| if ( not $entries->{ $entry }->{ obsolete } ) { |
| $bulk .= sprintf( " %-40s ", $entry ); |
| my $ordinal = $entries->{ $entry }->{ ordinal }; |
| if ( defined( $ordinal ) ) { |
| if ( $ordinal eq "DATA" ) { |
| $bulk .= "DATA"; |
| } else { |
| $bulk .= "\@" . $ordinal; |
| }; # if |
| }; # if |
| $bulk .= "\n"; |
| }; # if |
| }; # foreach |
| if ( defined( $output ) ) { |
| write_file( $output, \$bulk ); |
| } else { |
| print( $bulk ); |
| }; # if |
| |
| }; # sub generate_ouput |
| |
| # |
| # Parse command line. |
| # |
| |
| my $input; # The name of input file. |
| my $output; # The name of output file. |
| my %defs; |
| |
| get_options( |
| "output=s" => \$output, |
| "D|define=s" => |
| sub { |
| my ( $opt_name, $opt_value ) = @_; |
| my ( $def_name, $def_value ); |
| if ( $opt_value =~ m{\A(.*?)=(.*)\z} ) { |
| ( $def_name, $def_value ) = ( $1, $2 ); |
| } else { |
| ( $def_name, $def_value ) = ( $opt_value, 1 ); |
| }; # if |
| $defs{ $def_name } = $def_value; |
| }, |
| ); |
| |
| if ( @ARGV == 0 ) { |
| cmdline_error( "Not enough arguments." ); |
| }; # if |
| if ( @ARGV > 1 ) { |
| cmdline_error( "Too many arguments." ); |
| }; # if |
| $input = shift( @ARGV ); |
| |
| # |
| # Work. |
| # |
| |
| my %data = parse_input( $input, %defs ); |
| %data = process( %data ); |
| generate_output( %data, $output ); |
| exit( 0 ); |
| |
| __END__ |
| |
| # |
| # Embedded documentation. |
| # |
| |
| =pod |
| |
| =head1 NAME |
| |
| B<generate-def.pl> -- Generate def file for OpenMP RTL. |
| |
| =head1 SYNOPSIS |
| |
| B<generate-def.pl> I<OPTION>... I<file> |
| |
| =head1 OPTIONS |
| |
| =over |
| |
| =item B<--define=>I<name>[=I<value>] |
| |
| =item B<-D> I<name>[=I<value>] |
| |
| Define specified name. If I<value> is omitted, I<name> is defined to 1. If I<value> is 0 or empty, |
| name is B<not> defined. |
| |
| =item B<--output=>I<file> |
| |
| =item B<-o> I<file> |
| |
| Specify output file name. If option is not present, result is printed to stdout. |
| |
| =item B<--doc> |
| |
| =item B<--manual> |
| |
| Print full help message and exit. |
| |
| =item B<--help> |
| |
| Print short help message and exit. |
| |
| =item B<--usage> |
| |
| Print very short usage message and exit. |
| |
| =item B<--verbose> |
| |
| Do print informational messages. |
| |
| =item B<--version> |
| |
| Print version and exit. |
| |
| =item B<--quiet> |
| |
| Work quiet, do not print informational messages. |
| |
| =back |
| |
| =head1 ARGUMENTS |
| |
| =over |
| |
| =item I<file> |
| |
| A name of input file. |
| |
| =back |
| |
| =head1 DESCRIPTION |
| |
| The script reads input file, process conditional directives, checks content for consistency, and |
| generates ouptput file suitable for linker. |
| |
| =head2 Input File Format |
| |
| =over |
| |
| =item Comments |
| |
| # It's a comment. |
| |
| Comments start with C<#> symbol and continue to the end of line. |
| |
| =item Conditional Directives |
| |
| %ifdef name |
| %ifndef name |
| %endif |
| |
| A part of file surrounded by C<%ifdef I<name>> and C<%endif> directives is a conditional part -- it |
| has effect only if I<name> is defined in the comman line by B<--define> option. C<%ifndef> is a |
| negated version of C<%ifdef> -- conditional part has an effect only if I<name> is B<not> defined. |
| |
| Conditional parts may be nested. |
| |
| =item Export Definitions |
| |
| symbol |
| symbol ordinal |
| symbol DATA |
| |
| Symbols starting with C<omp_> or C<kmp_> must have ordinal specified. They are subjects for special |
| processing: each symbol generates two output lines: original one and upper case version. The ordinal |
| number of the second is original ordinal increased by 1000. |
| |
| =item Obsolete Symbols |
| |
| - symbol |
| - symbol ordinal |
| - symbol DATA |
| |
| Obsolete symbols look like export definitions prefixed with minus sign. Obsolete symbols do not |
| affect the output, but obsolete symbols and their ordinals cannot be (re)used in export definitions. |
| |
| =back |
| |
| =head1 EXAMPLES |
| |
| $ generate-def.pl -D stub -D USE_TCHECK=0 -o libguide.def dllexport |
| |
| =cut |
| |
| # end of file # |
| |