fix codetest failure - ASSERT_ARGS does not have a ; after and
[parrot.git] / tools / dev / parrot_api.pl
blobdadb664ab78f62ea9cdacb03a7174516d0cbc121
1 #! perl
2 # Copyright (C) 2004-2007, Parrot Foundation.
3 # $Id$
5 =head1 NAME
7 tools/dev/parrot_api.pl - Verify Parrot API (symbols)
9 =head1 SYNOPSIS
11 % perl tools/dev/parrot_api.pl [libfile]
13 =head1 DESCRIPTION
15 Displays the API (the visible symbols, code or data) of the Parrot lib.
17 First lists the Parrot public embedding API as described in the public
18 headers F<include/parrot/embed.h> and F<include/parrot/extend.h> (the
19 API is detected using pattern C</^\w+\s+(Parrot_\w+)\(/>), then finds
20 out the visible symbols in the Parrot lib (by default
21 F<blib/lib/libparrot.a>), and then cross-references the dubious API
22 symbols according to the below categories. Each symbol is listed with
23 the object file it was found in.
25 Good things are listed with C<+++> (at the moment the only good thing
26 is Parrot APIs definitions their declarations), bad things are listed
27 with C<--->.
29 =over 4
31 =item Missing Parrot API
33 The API is listed in the public headers but not defined in the Parrot lib.
35 Either the API listing is wrong or the implementation is missing.
37 =item No Parrotish Prefix
39 The API is implemented but has no C<Parrot_> prefix (or prefix deemed
40 Parroty enough, like C<PDB_>, C<PF_>, C<PIO_>, and C<PackFile>).
42 If code: see L</"Public or Private">.
44 If data: at least consider limiting its scope by making it file
45 static or function static, but see L</"Data is not an API">.
47 =item No Parrot API
49 The API is defined in the lib but not defined in the public headers.
51 If code: see L</"Public or Private">.
53 If data: see L</"Data is not an API">.
55 =item Uninitialized Modifiable Data
57 Data symbol that is not initialized with any data.
59 See L</"Data is not an API">.
61 =item Initialized Modifiable Data
63 Data symbol that is initialized with data, but modifiable.
65 See L</"Data is not an API">.
67 =back
69 =head1 RULES
71 You can sometimes use C preprocessor defines to shorten the API names
72 but please do carefully contain the effect so that only the Parrot
73 itself sees those shortened definitions, the defines must not leak
74 to the outside world.
76 =head2 Public or Private
78 If the API is really meant to be public, prefix it with <Parrot_>,
79 or something else specific enough, preferably specific to Parrot,
80 not some generic term. Currently acceptable prefixes are
81 C</^(Parrot|PDB|PF|PIO|PackFile)_/>.
83 If the API is not meant to be public, considering making it private
84 (file static, and prefix it with C<S_>, but still do have a prototype
85 for it).
87 =head2 Data is not an API
89 Consider making the data const(ant), or moving it into the heap (and
90 accessed through a real API that takes care of synchronization, data
91 as such is not a good API unless it's constant).
93 Think multithreaded access, or just plain reentrancy (think recursion).
95 Often you can collect a group of data fields into a "context" (or
96 "descriptor", "handle", "session", "state") structure that is either
97 explicitly passed between functions or implicitly retrieved from a
98 global API. Encapsulating data like this is good: it allows passing,
99 synchronzing, and duplicating state much easier, and makes it clearer
100 into which context the data belongs.
102 For APIs purely internal to Parrot, try using C<const> in function
103 prototypes as often as possible to catch inadvertent data modification
104 by callers. For APIs that cross into the operating system and/or
105 external libraries, you usually cannot go overly C<const> because of
106 portability issues.
108 Make your strings and arrays of strings (or similar inlined data)
109 C<const> -- for the latter, remember that you need two consts:
111 const char* const foo[] = { "foo", "bar" };
113 =head1 DEPENDENCIES
115 Uses F<tools/dev/nm.pl> to list the symbols.
117 =head1 TODO
119 =over 4
121 =item *
123 Write a pollution detector also for the C preprocessor: also in that
124 namespace, Parrot should only present Parrot_ (and similar) symbols to
125 the outside world. Be careful to only scan Parrot's defines, not
126 system's or third parties'.
128 =back
130 =head1 HISTORY
132 Author: Jarkko Hietaniemi.
134 =cut
136 use strict;
137 use warnings;
139 my $Obj;
141 $Obj = shift(@ARGV) unless defined $Obj;
142 $Obj = 'blib/lib/libparrot.a' unless defined $Obj;
143 die "$0: '$Obj': No such file\n" unless -f $Obj;
145 my %ParrotAPI;
147 $| = 1;
149 my @H = qw(include/parrot/embed.h include/parrot/extend.h);
151 for my $h (@H) {
152 if ( open( my $H, '<', $h ) ) {
153 while (<$H>) {
154 if (/^\w+\s+(Parrot_\w+)\(/) {
155 $ParrotAPI{$1}++;
158 close($H);
160 else {
161 die "$0: Header '$h': $!\n";
165 my @ParrotAPI = sort keys %ParrotAPI;
167 die "$0: No API found in @H\n" unless @ParrotAPI;
169 printf "=== @H: %d interfaces ===\n", scalar @ParrotAPI;
171 my %Code;
172 my %DataB;
173 my %DataD;
174 my %DataR;
175 my %Undef;
176 my %API;
178 if ( open( my $NM, '<', "perl tools/dev/nm.pl -BDo '$Obj' |" ) ) {
179 while (<$NM>) {
180 my ( $o, $s, $v ) = split;
181 $API{$s} = $o;
182 if ( $v eq 'T' ) {
183 $Code{$s} = $o;
185 elsif ( $v =~ /[BDR]/ ) {
186 if ( $v eq 'B' ) {
187 $DataB{$s} = $o;
189 elsif ( $v eq 'D' ) {
190 $DataD{$s} = $o;
192 elsif ( $v eq 'R' ) {
193 $DataR{$s} = $o;
196 elsif ( $v eq 'U' ) {
197 $Undef{$s} = $o;
200 close($NM);
202 else {
203 die "$0: nm.pl -Bgo '$Obj': $!\n";
205 for my $api ( keys %API ) {
206 delete $API{$api} unless exists $Code{$api}; # Not ours.
209 printf "+++ Parrot API: %d +++\n", scalar @ParrotAPI;
210 if (@ParrotAPI) {
211 for my $api (@ParrotAPI) {
212 printf "%s\t%s\tOK\n", $api, $API{$api} || "-";
216 printf "=== $Obj: %d interfaces ===\n", scalar keys %API;
218 my @API = sort keys %API;
220 my @NoParrotAPI = grep { !exists $API{$_} } sort keys %ParrotAPI;
221 my @NoParrotPrefix;
222 my @UnParrotAPI;
224 my $ParrotPrefix = qr/^(Parrot|PDB|PF|PIO|PackFile)_/;
226 for my $api (@API) {
227 unless ( $api =~ $ParrotPrefix ) {
228 push @NoParrotPrefix, $api;
230 unless ( exists $ParrotAPI{$api} || $api =~ $ParrotPrefix ) {
231 push @UnParrotAPI, $api;
235 if (@NoParrotAPI) {
236 printf "--- Missing Parrot API: %d ---\n", scalar @NoParrotAPI;
237 for my $api (@NoParrotAPI) {
238 printf "%s\t%s\tMISSING_API\n", $api, "-";
242 if (@NoParrotPrefix) {
243 printf "--- No Parrot prefix: %d ---\n", scalar @NoParrotPrefix;
244 for my $api (@NoParrotPrefix) {
245 printf "%s\t%s\tBAD_PREFIX\n", $api, $API{$api};
249 if (@UnParrotAPI) {
250 printf "--- No Parrot API: %d ---\n", scalar @UnParrotAPI;
251 for my $api (@UnParrotAPI) {
252 printf "%s\t%s\tNO_API\n", $api, $API{$api};
256 if ( keys %DataB ) {
257 printf "--- Uninitialized Modifiable Data: %d ---\n", scalar keys %DataB;
258 for my $api ( sort keys %DataB ) {
259 printf "%s\t%s\tUMD\n", $api, $DataB{$api};
263 if ( keys %DataD ) {
264 printf "--- Initialized Modifiable Data: %d ---\n", scalar keys %DataD;
265 for my $api ( sort keys %DataD ) {
266 printf "%s\t%s\tIMD\n", $api, $DataD{$api};
270 exit(0);
272 # Local Variables:
273 # mode: cperl
274 # cperl-indent-level: 4
275 # fill-column: 100
276 # End:
277 # vim: expandtab shiftwidth=4: