• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#! /usr/bin/perl
2##
3## Copyright 2019 The Android Open Source Project
4##
5## Licensed under the Apache License, Version 2.0 (the "License");
6## you may not use this file except in compliance with the License.
7## You may obtain a copy of the License at
8##
9##      http://www.apache.org/licenses/LICENSE-2.0
10##
11## Unless required by applicable law or agreed to in writing, software
12## distributed under the License is distributed on an "AS IS" BASIS,
13## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14## See the License for the specific language governing permissions and
15## limitations under the License.
16
17use File::Basename;
18
19## mockcify version
20##
21## 0.6.0 Replace `extern` with `include` for mock_function_count_map
22##
23## 0.5.0 Add compilation check
24##
25## 0.4.0 Second re-write
26##
27## 0.3.2 Remove pragma from source file
28##
29## 0.3.1 Statically link return value to prevent 'this' pointer in function
30##
31## 0.3.0 Re-write parser.
32##
33## 0.2.1 Compilation units only include types and a single related header file
34##       Alphabetically sort functions by return value
35##       Add non-primative return data values in structure
36##
37## 0.2.0 First version
38##
39my $VERSION = "0.6.0";
40
41use strict;
42use warnings;
43
44my $YEAR = "2023";
45my $TOKEN = "MOCKCIFY_TOKEN";
46my $MOCKCIFY_BRACKET_GROUP = "MOCKCIFY_BRACKET_GROUP";
47my $CLANG_FORMAT = "/usr/bin/clang-format-13";
48my $CC = "g++";
49my $LIBCHROME = "../../../../external/libchrome/";
50my $COMPILE_SCREEN_ENABLED = 0;
51
52my @structs;
53
54my %function_signature;
55my %function_return_types;
56my @function_names;
57my %function_params;
58my %function_param_names;
59my %function_param_types;
60
61sub clang_format {
62    return `$CLANG_FORMAT --style="{ColumnLimit: 10000, PointerAlignment: Left, PointerBindsToType: true, FixNamespaceComments: true }"`;
63}
64
65## Create a temp directory for any cruft
66my $TMPDIR="/tmp/mockcify";
67system("mkdir -p $TMPDIR");
68my $OUTDIR = "$TMPDIR/out/";
69system("mkdir -p $OUTDIR");
70my $INCDIR = "$TMPDIR/include/";
71system("mkdir -p $INCDIR");
72
73if (scalar(@ARGV == 0)) {
74  printf(STDERR "ERROR Must supply at least one argument\n");
75  exit 1;
76}
77
78my $arg = shift @ARGV;
79## Check only argument for debug vector
80if ($arg =~ /--cla[ng]/) {
81    exit print clang_format();
82} elsif ($arg =~ /--f[ilter]/) {
83    exit print read_stdin_and_filter_file();
84} elsif ($arg =~ /--l[ines]/) {
85    exit print filter_lines(read_stdin_and_filter_file());
86} elsif ($arg =~ /--i[nfo]/) {
87    my ($incs, $types, $funcs) = parse_info(filter_lines(read_stdin_and_filter_file()));
88    exit print @{$incs}, @{$types}, @{$funcs};
89} elsif ($arg =~ /--co[mpile]/) {
90    exit compilation_screen("mock_" . shift @ARGV);
91} elsif ($arg =~ /--cle[an]/) {
92    exit system("mv $TMPDIR $TMPDIR.deleted");
93} elsif ($arg =~ /--u[nittest]/) {
94    print(STDERR "unit testing device");
95}
96
97sub help {
98    print <<EOF
99 Usage:
100  Specify a namespace on the command line for the shared structure data.
101  Then pipe the C file on stdin and one source and one header file will
102  be created based upon the namespace convention
103
104 mockcify.pl stack_l2cap_api < stack/l2cap/l2c_api.cc
105
106 Output files:
107  mock_stack_l2cap_api.cc
108  mock_stack_l2cap_api.h
109
110  The tool is not capable of parsing C++ and a workaround is to remove
111  C++ in the source prior to mock-C-fying the source.
112
113EOF
114}
115
116## Only single arg is taken
117my $namespace = $arg;
118
119if ($namespace =~ /^--/) {
120    print(STDERR "ERROR Halting due to ill-formed namespace expression \'$namespace\'\n");
121    exit -1;
122}
123
124###
125### Phase 0: Prepare input and output file streams
126###
127
128## Default to stdout
129my $FH_SRC;
130my $FH_HDR;
131
132my $src_filename;
133my $hdr_filename;
134## If namepace specified then write to that source and header
135if ($namespace eq "TESTING") {
136  $FH_SRC = *STDOUT;
137  $FH_HDR = *STDOUT;
138} else {
139  $src_filename="mock_" . $namespace . ".cc";
140  $hdr_filename="mock_" . $namespace . ".h";
141
142  open($FH_SRC, ">", $OUTDIR .$src_filename)
143    or die $!;
144
145  open($FH_HDR, ">", $OUTDIR .$hdr_filename)
146    or die $!;
147}
148
149###
150### Phase 1: Read input file and apply single line filtering
151###
152my $text = read_stdin_and_filter_file();
153
154###
155### Phase 2: Apply Multiline filters
156###
157$text = filter_lines($text);
158
159##
160## Phase 3: Extract required mock information
161##
162my ($includes_ref, $typedefs_ref, $functions_ref, $usings_ref,  $namespaces_ref) = parse_info($text);
163my @includes = @{$includes_ref};
164my @typedefs = @{$typedefs_ref};
165my @functions = @{$functions_ref};
166my @namespaces = @{$namespaces_ref};
167my @usings = @{$usings_ref};
168
169@includes = reject_include_list(@includes);
170
171@functions = grep { parse_function_into_components ($_) } @functions;
172
173##
174## Phase 4: Output the mocks source and header
175##
176print_source($FH_SRC);
177print_header($FH_HDR);
178
179close ($FH_SRC);
180close ($FH_HDR);
181
182## Format the final source code files
183if (defined $src_filename) {
184  system("clang-format", "-i", $OUTDIR . $src_filename);
185  system("clang-format", "-i", $OUTDIR . $hdr_filename);
186}
187
188print(STDERR "Generated files:", $OUTDIR . $src_filename, " ", $OUTDIR . $hdr_filename, "\n");
189
190if ($COMPILE_SCREEN_ENABLED) {
191  my $rc = compilation_screen("mock_" . $namespace);
192  exit ($rc == 256) ?1 : 0;
193}
194
195sub reject_include_list {
196    my @incs = ();
197    foreach (@_) {
198      next if (/init_flags/);
199      push(@incs, $_);
200    }
201    return @incs;
202}
203
204sub compile_screen_failed {
205    my $src = shift @_;
206    print STDERR <<EOF
207    MOCK Compilation is EXPERIMENTAL ONLY
208
209    ERROR Failed to compile \'$src\' NOTE: This does not mean
210    the mock is unusable as the tool only screens the compilation.
211
212    There could be one of 3 problems:
213    1. Undeclared external surface or dependency
214    2. C++ code or namespaces mixed in with C code
215    3. An issue with proper mock'ing with mockcify.
216EOF
217}
218
219sub compilation_screen {
220    my $base= shift @_;
221    my $src=$base . ".cc";
222    my $hdr=$base . ".h";
223
224    ## Verious external or generated header not needed for mocks
225    foreach((
226            "test/mock/mock.h",
227            "src/init_flags.rs.h",
228            "src/message_loop_thread.rs.h",
229            "android/hardware/bluetooth/audio/2.2/IBluetoothAudioProvidersFactory.h",
230            "android/hardware/bluetooth/audio/2.2/types.h",
231        )) {
232        system("mkdir -p $INCDIR". dirname($_));
233        system("touch $INCDIR/$_");
234    }
235    my @incs = (
236        $INCDIR,
237        $LIBCHROME,
238        ".",
239        "audio_hal_interface/",
240        "include/",
241        "stack/include/",
242        "btif/include/",
243        "internal_include",
244        "osi/include/",
245        "test/mock/",
246        "types/",
247    );
248    my @defs = (
249        "HAS_NO_BDROID_BUILDCFG",
250    );
251
252    my $link="test/mock/$hdr";
253    unlink "$INCDIR/$link";
254    symlink "$OUTDIR/$hdr", "$INCDIR/$link";
255    system("$CC -c -std=c++17 -o /dev/null -D" . join(" -D", @defs) . " -I" . join(" -I", @incs) . " $OUTDIR/$src");
256    my $rc = $?;
257         ($? == 0)
258         ? printf(STDERR "SUCCESS Compiled unit \'$src\'\n")
259         : compile_screen_failed($src);
260    return $rc;
261}
262
263###
264### Phase 4.1: Print the source compilation unit and the associated structues
265###
266sub print_source {
267  my $FH = shift @_;
268  print_copyright($FH);
269  print_generated_note($FH);
270  print_mock_decl_src($FH);
271
272  print_mock_header_include($FH);
273  print_usings($FH);
274  print_internal_structs($FH);
275  print_source_namespace_structs($FH);
276  print_static_return_values($FH);
277  print_mocked_functions($FH);
278
279  print $FH "// END mockcify generation\n";
280}
281
282###
283### Phase 4.2 Print the header unit to be included with the test
284###
285sub print_header {
286  my $FH = shift @_;
287  print_copyright($FH);
288  print_pragma($FH);
289  print_generated_note($FH);
290  print_mock_decl_hdr($FH);
291
292  print_includes($FH);
293  print_usings($FH);
294  print_defs($FH);
295  print_header_test_mock_namespace_structs($FH);
296  print $FH "// END mockcify generation";
297}
298
299sub get_function_param_names {
300    my $name = shift @_;
301    my @param_names;
302    foreach (0..$#{$function_param_names{$name}}) {
303        my $param_name = $function_param_names{$name}[$_];
304        my $param_type = $function_param_types{$name}[$_];
305
306        if ($param_type =~ /unique_ptr/) {
307            ## Wrap name in a move operation
308            push(@param_names, "std::move($param_name)");
309        } else {
310            push(@param_names, $param_name);
311        }
312    }
313    return join(',', @param_names);
314}
315
316##
317## Parse a function signature into 4 basic components and insert into
318## the global hashes and arrays.
319##  1. @function return type
320##  2. @function name
321##  3. %param types
322##  4. %param names
323##
324sub parse_function_into_components {
325  my $function = shift @_;
326  ## Ensure this is really a function string
327  assert(substr $function, -1 eq ')');
328
329  ## Split on first occurrence of open paren to get return
330  ## type and name of function
331  my ($return_type_and_name, $params) = split '\(', $function, 2;
332  if (!defined($params)) {
333      printf(STDERR "WARNING \'params\' is undefined \"$params\" function:\'$function\'\n");
334      return 0;
335  }
336  ## Remove input params closing paren
337  $params=~ s/\).*$//;
338
339  ## Parse the return type and function name
340  my ($return_type, $name) = $return_type_and_name =~ /(.*)\s(.*)/;
341
342  if (!defined($name)) {
343      printf(STDERR "WARNING \'name\' is undefined \"$return_type_and_name\" a [con|des]tructor ?\n");
344      return 0;
345  }
346  if ($name =~ /::/) {
347      printf(STDERR "WARNING \'name\' is unhandled class method \'$name\'\n");
348      return 0;
349  }
350
351  ## Store away complete function signature
352  $function_signature{$name} = $function;
353
354  ## Store away the parameter type and names
355  chomp($params);
356  $function_params{$name} = $params;
357
358  ## Parse the parameter types and names
359  my @param_types;
360  my @param_names;
361
362  ## Skip when void keyword used for no parameters
363  if ($params ne "void") {
364    foreach (split ',', $params) {
365      s/^\s+//;
366      if (/\(/) {
367        ## TODO Parameter is a C style function
368        my @vars;
369        my @f = split /[\(\)]/;
370        push(@vars, substr $f[1], 1);
371      } else {
372        ## Store the type and name
373        my ($type, $name) = /(.*)\s(.*)/;
374        push(@param_names, $name);
375        push(@param_types, $type);
376      }
377    }
378  }
379  push(@function_names, $name);
380  $function_return_types{$name} = $return_type;
381  $function_param_types{$name} = \@param_types;
382  $function_param_names{$name} = \@param_names;
383  return 1;
384}
385
386##
387## Read a file from stdin and does a first pass simple
388## filtering that removes single lines.
389##
390sub read_stdin_and_filter_file {
391  my @filtered_lines;
392  my @clang_format=clang_format();
393  foreach (@clang_format) {
394    ## Update header guards with compiler #pragma for proper
395    ## decision processing of header or source
396    s/^#ifndef [A-Z_0-9]+_H/#pragma once/;
397
398    unless (/^extern/
399        or /^#define /
400        or / = \{/
401        or /^#if /
402        or /^constexpr/
403        or /^#ifdef/
404        or /^#ifndef/
405        or /^#else/
406        or /^enum/
407        or /^static.*;$/
408        or /^#endif/) {
409        ## Remove any single line C style comments
410        s:/\*.*\*/::;
411        push(@filtered_lines, $_);
412      }
413  }
414  return join('', @filtered_lines);
415}
416
417sub filter_lines {
418  $_ = shift @_;
419  ## Remove anonymous namespaces
420  ## $text =~ s/namespace \{.*\n\} \/\/ namespace/\n/sg;
421  s/namespace \{.*\n\} \/\/ namespace?/\n/sg;
422  s/namespace \{.?\n\}/\n/g;
423  ## Remove C style comments
424  s/\s*\/\*(?:(?!\*\/).)*\*\/\n?/\n/sg;
425  ## Remove Cpp style comments
426  s/\s*\/\/.*//g;
427  ## Remove unnecessary bluetooth osi specific modifier
428  s/UNUSED_ATTR//g;
429  ## Modify internally defined structure typedefs
430  s/typedef struct \{.*?\n\} (\w+);/typedef struct $MOCKCIFY_BRACKET_GROUP $1;/sg;
431  ## Modify internally defined structure typedefs
432  s/typedef struct (\w+) \{.*?\n\} (\w+);/struct $1 $MOCKCIFY_BRACKET_GROUP;/sg;
433  ## Modify internally defined structures
434  s/struct (\w+) \{.*?\n\};/struct $1 $MOCKCIFY_BRACKET_GROUP;/sg;
435  ## Remove lines only with spaces
436  s/^\s+$//sg;
437  return $_;
438}
439
440sub parse_info {
441    if (/\n#pragma once\n/) {
442        return parse_info_header(shift @_);
443    } else {
444        return parse_info_source(shift @_);
445    }
446}
447
448sub parse_info_header {
449  my (@includes, @typedefs, @functions, @usings, @namespaces);
450  foreach (split('\n')) {
451      chomp();
452      if (/^ /) {
453      } elsif (/^#include /) {
454          push(@includes, $_);
455      } elsif (/^typedef /) {
456          push @typedefs, $_;
457      } elsif ($_ =~ /^ *$/) {
458          # Skip function body indicated by indentation
459      } elsif ($_ =~ /^}/) {
460          # Skip function curly bracket closure
461      } elsif (/^namespace/) {
462          push @namespaces, $_;
463      } elsif (/\(/) {
464          # Add function signature
465          chomp();
466          ## Remove all function body after signature
467          s/{.*$//;
468          ## Remove whitespace on both ends
469          s/^\s+|\s+$//g;
470          ## Ignore locally linked functions
471          next if (/^static/);
472          ## Reduce all remaining whitespace to a single space
473          s/\s+/ /g;
474          ## Remove any semi colons
475          s/;//g;
476          push(@functions, "$_\n");
477      } else {
478          # Not a function. skip
479      }
480  }
481  printf(STDERR "Parsed HEADER lines includes:%d typedefs:%d functions:%d\n",
482      scalar(@includes), scalar(@typedefs), scalar(@functions));
483  return (\@includes, \@typedefs, \@functions, \@usings, \@namespaces);
484}
485
486sub parse_info_source{
487  my @s = split('\n', $_);
488  my (@includes, @typedefs, @functions, @usings, @namespaces);
489  foreach (@s) {
490      chomp();
491      if (/^ /) {
492      } elsif (/^#include /) {
493          push @includes, $_;
494      } elsif (/^typedef /) {
495          push @typedefs, $_;
496      } elsif (/^using /) {
497          push @usings, $_;
498      } elsif (/^namespace/) {
499          push @namespaces, $_;
500      } elsif ($_ =~ /^ *$/) {
501          # Skip function body indicated by indentation
502      } elsif ($_ =~ /^}/) {
503          # Skip function curly bracket closure
504        } elsif (/\{/) {
505          # Add function signature
506          chomp();
507          ## Remove all function body after signature
508          s/{.*$//;
509          ## Remove whitespace on both ends
510          s/^\s+|\s+$//g;
511          ## Ignore locally linked functions
512          next if (/^static/);
513          ## Reduce all remaining whitespace to a single space
514          s/\s+/ /g;
515          push(@functions, "$_\n");
516      } else {
517          # Not a function. skip
518      }
519  }
520  printf(STDERR "Parsed SOURCE lines includes:%d typedefs:%d functions:%d\n",
521      scalar(@includes), scalar(@typedefs), scalar(@functions));
522  return (\@includes, \@typedefs, \@functions, \@usings, \@namespaces);
523}
524
525## Returns the default type specified by the function return type.
526## These are processed in priority order.
527sub get_default_return_value_from_type {
528  $_ = shift @_;
529  assert($_ ne '');
530  if (/^bool/) {
531    return "false";
532  } elsif (/\*$/ or /^std::unique_ptr/ or /^std::shared_ptr/) {  ## Pointer return val
533    return "nullptr";
534  } elsif (/^void/) {
535    return "";
536  } elsif (/^std::string/) {
537    return "std::string()";
538  } elsif (/^std::list\<entry_t\>::iterator/) {
539    return "static std::list<entry_t> v";
540  } elsif (/^std::list\<section_t\>::iterator/) {
541    return "std::list<section_t>";
542  } elsif (/reactor_status_t/) {
543    return "REACTOR_STATUS_DONE";
544  } elsif (/tL2CAP_LE_RESULT_CODE/) {
545    return "L2CAP_LE_RESULT_CONN_OK";
546  } elsif (/std::vector/) {
547    return "retval";
548  } elsif (/tBT_TRANSPORT/) {
549    return "BT_TRANSPORT_BR_EDR";
550  } elsif (/tSDP_STATUS/) {
551    return "SDP_SUCCESS";
552  } elsif (/tGATT_STATUS/) {
553    return "GATT_SUCCESS";
554  } elsif (/tHID_STATUS/) {
555    return "HID_SUCCESS";
556  } elsif (/future_t\*/) {
557    return "FUTURE_FAIL";
558  } elsif(/bt_status_t/) {
559    return "BT_STATUS_SUCCESS";
560  } elsif(/.*module_t\*/) {
561    return "nullptr";
562  } elsif(/btav_a2dp_codec_index_t/) {
563    return "BTAV_A2DP_CODEC_INDEX_SOURCE_MIN";
564  } else {
565    ## Decay to int type
566    return "0";
567  }
568}
569
570##
571## Various print output boilerplate
572###
573sub print_copyright {
574  my $FH = shift @_;
575print $FH <<EOF
576/*
577 * Copyright $YEAR The Android Open Source Project
578 *
579 * Licensed under the Apache License, Version 2.0 (the "License");
580 * you may not use this file except in compliance with the License.
581 * You may obtain a copy of the License at
582 *
583 *      http://www.apache.org/licenses/LICENSE-2.0
584 *
585 * Unless required by applicable law or agreed to in writing, software
586 * distributed under the License is distributed on an "AS IS" BASIS,
587 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
588 * See the License for the specific language governing permissions and
589 * limitations under the License.
590 */
591EOF
592}
593
594## Print body of each function
595sub print_mocked_functions {
596  my $FH = shift @_;
597  print $FH <<EOF;
598// Mocked functions, if any
599EOF
600  foreach my $name (sort @function_names) {
601      my $return_type = $function_return_types{$name};
602      assert($return_type ne '');
603
604      my $return_keyword = $return_type eq "void" ? "" : "return";
605      my $function_param_names = get_function_param_names($name);
606
607      print $FH <<EOF;
608$function_signature{$name} {
609    inc_func_call_count(__func__);
610    ${return_keyword} test::mock::${namespace}::${name}($function_param_names);
611}
612EOF
613  }
614  print $FH <<EOF;
615// Mocked functions complete
616EOF
617}
618
619sub print_static_return_values {
620  my $FH = shift @_;
621  print $FH <<EOF;
622// Mocked function return values, if any
623namespace test {
624namespace mock {
625namespace $namespace {
626
627EOF
628  foreach my $name (sort @function_names) {
629      $name =~ s/\s+$//;
630      my $return_type = $function_return_types{$name};
631      assert($return_type ne '');
632
633      next if ($return_type eq "void");
634      my $default_return_value = get_default_return_value_from_type($return_type);
635      print $FH "${return_type} ${name}::return_value = ${default_return_value};\n";
636  }
637  print $FH <<EOF;
638
639} // namespace $namespace
640} // namespace mock
641} // namespace test
642
643EOF
644}
645
646##
647## Collection of mocked functions
648sub print_source_namespace_structs {
649  my $FH = shift @_;
650  print $FH <<EOF;
651namespace test {
652namespace mock {
653namespace $namespace {
654
655// Function state capture and return values, if needed
656EOF
657    foreach my $name (sort @function_names) {
658      print $FH "struct $name $name;\n";
659    }
660    print $FH <<EOF;
661
662} // namespace $namespace
663} // namespace mock
664} // namespace test
665
666EOF
667}
668
669##
670##  Print the definitions of the various structures for the header files
671##
672sub print_header_test_mock_namespace_structs {
673  my $FH = shift @_;
674  print $FH <<EOF;
675namespace test {
676namespace mock {
677namespace $namespace {
678
679// Shared state between mocked functions and tests
680EOF
681  foreach my $name (sort @function_names) {
682      my $input_params = $function_params{$name};
683      my $return_type = $function_return_types{$name};
684      my @param_names = $function_param_names{$name};
685      assert($return_type ne '');
686
687      my $function_param_names = get_function_param_names($name);
688      my $return_keyword = $return_type eq "void" ? "" : "return";
689      my $return_statement = $return_type eq "void" ? "" : "return return_value;";
690      my $return_definition = $return_type eq "void" ? "" : "static $return_type return_value;";
691
692print $FH <<EOF;
693// Name: $name
694// Params: $input_params
695// Return: $return_type
696struct $name {
697EOF
698       if ($return_definition ne "") {
699           print $FH "$return_definition\n";
700       }
701print $FH <<EOF;
702    std::function<$return_type($input_params)> body{[]($input_params){$return_statement}};
703    $return_type operator()($input_params) { ${return_keyword} body($function_param_names);};
704};
705extern struct $name $name;
706
707EOF
708    }
709print $FH <<EOF;
710} // namespace $namespace
711} // namespace mock
712} // namespace test
713
714EOF
715}
716
717sub print_pragma {
718  my $FH = shift @_;
719print $FH <<EOF
720#pragma once
721
722EOF
723}
724
725sub print_generated_note {
726  my $FH = shift @_;
727  my $gen = scalar(@functions);
728print $FH <<EOF;
729/*
730 * Generated mock file from original source file
731 *   Functions generated:$gen
732 *
733 *  mockcify.pl ver $VERSION
734 */
735
736EOF
737}
738
739sub print_usings {
740  my $FH = shift @_;
741print $FH <<EOF;
742// Original usings
743EOF
744  foreach (sort @usings) {
745    print $FH $_, "\n";
746  }
747  print($FH "\n");;
748}
749
750sub print_includes {
751  my $FH = shift @_;
752  print $FH <<EOF;
753// Original included files, if any
754// NOTE: Since this is a mock file with mock definitions some number of
755//       include files may not be required.  The include-what-you-use
756//       still applies, but crafting proper inclusion is out of scope
757//       for this effort.  This compilation unit may compile as-is, or
758//       may need attention to prune from (or add to ) the inclusion set.
759EOF
760  foreach (sort @includes) {
761    print $FH $_, "\n";
762  }
763  print($FH "\n");;
764}
765
766sub print_mock_header_include {
767  my $FH = shift @_;
768  print $FH <<EOF;
769// Mock include file to share data between tests and mock
770#include "test/mock/mock_${namespace}.h"
771
772EOF
773}
774
775sub print_mock_decl_hdr {
776  my $FH = shift @_;
777print $FH <<EOF;
778#include <cstdint>
779#include <functional>
780#include <map>
781#include <string>
782
783#include "test/common/mock_functions.h"
784
785EOF
786}
787
788sub print_mock_decl_src {
789  my $FH = shift @_;
790print $FH <<EOF;
791#include <cstdint>
792#include <functional>
793#include <map>
794#include <string>
795
796EOF
797}
798
799sub print_defs {
800  my $FH = shift @_;
801  print $FH <<EOF;
802// Mocked compile conditionals, if any
803
804EOF
805}
806
807sub print_internal_structs {
808  my $FH = shift @_;
809  print $FH <<EOF;
810// Mocked internal structures, if any
811EOF
812
813  foreach (sort @structs) {
814    print $FH $_,"\n"};
815  print $FH "\n";
816}
817
818sub assert {
819    my ($condition, $msg) = @_;
820    return if $condition;
821    if (!$msg) {
822        my ($pkg, $file, $line) = caller(0);
823        open my $fh, "<", $file;
824        my @lines = <$fh>;
825        close $fh;
826        $msg = "$file:$line: " . $lines[$line - 1];
827    }
828    die "Assertion failed: $msg";
829}
830