build-web-examples.pl 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. #!/usr/bin/perl -w
  2. # Simple DirectMedia Layer
  3. # Copyright (C) 1997-2024 Sam Lantinga <[email protected]>
  4. #
  5. # This software is provided 'as-is', without any express or implied
  6. # warranty. In no event will the authors be held liable for any damages
  7. # arising from the use of this software.
  8. #
  9. # Permission is granted to anyone to use this software for any purpose,
  10. # including commercial applications, and to alter it and redistribute it
  11. # freely, subject to the following restrictions:
  12. #
  13. # 1. The origin of this software must not be misrepresented; you must not
  14. # claim that you wrote the original software. If you use this software
  15. # in a product, an acknowledgment in the product documentation would be
  16. # appreciated but is not required.
  17. # 2. Altered source versions must be plainly marked as such, and must not be
  18. # misrepresented as being the original software.
  19. # 3. This notice may not be removed or altered from any source distribution.
  20. use warnings;
  21. use strict;
  22. use File::Basename;
  23. use Cwd qw(abs_path);
  24. use IPC::Open2;
  25. my $examples_dir = abs_path(dirname(__FILE__) . "/../examples");
  26. my $project = undef;
  27. my $emsdk_dir = undef;
  28. my $compile_dir = undef;
  29. my $output_dir = undef;
  30. sub usage {
  31. die("USAGE: $0 <project_name> <emsdk_dir> <compiler_output_directory> <html_output_directory>\n\n");
  32. }
  33. sub do_system {
  34. my $cmd = shift;
  35. $cmd = "exec /bin/bash -c \"$cmd\"";
  36. print("$cmd\n");
  37. return system($cmd);
  38. }
  39. sub do_mkdir {
  40. my $d = shift;
  41. if ( ! -d $d ) {
  42. print("mkdir '$d'\n");
  43. mkdir($d) or die("Couldn't mkdir('$d'): $!\n");
  44. }
  45. }
  46. sub build_latest {
  47. # Try to build just the latest without re-running cmake, since that is SLOW.
  48. print("Building latest version of $project ...\n");
  49. if (do_system("EMSDK_QUIET=1 source '$emsdk_dir/emsdk_env.sh' && cd '$compile_dir' && ninja") != 0) {
  50. # Build failed? Try nuking the build dir and running CMake from scratch.
  51. print("\n\nBuilding latest version of $project FROM SCRATCH ...\n");
  52. if (do_system("EMSDK_QUIET=1 source '$emsdk_dir/emsdk_env.sh' && rm -rf '$compile_dir' && mkdir '$compile_dir' && cd '$compile_dir' && emcmake cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel '$examples_dir/..' && ninja") != 0) {
  53. die("Failed to build latest version of $project!\n"); # oh well.
  54. }
  55. }
  56. }
  57. sub handle_example_dir {
  58. my $category = shift;
  59. my $example = shift;
  60. my @files = ();
  61. my $files_str = '';
  62. opendir(my $dh, "$examples_dir/$category/$example") or die("Couldn't opendir '$examples_dir/$category/$example': $!\n");
  63. my $spc = '';
  64. while (readdir($dh)) {
  65. next if not /\.c\Z/; # only care about .c files.
  66. my $path = "$examples_dir/$category/$example/$_";
  67. next if not -f $path; # only care about files.
  68. push @files, $path;
  69. $files_str .= "$spc$path";
  70. $spc = ' ';
  71. }
  72. closedir($dh);
  73. my $datafilestxtpath = "$examples_dir/$category/$example/datafiles.txt";
  74. my $datafiles_str = '';
  75. if ( -f $datafilestxtpath ) {
  76. open (my $datafilesh, '<', $datafilestxtpath) or die("Couldn't opendir '$datafilestxtpath': $!\n");
  77. $spc = '';
  78. while (<$datafilesh>) {
  79. chomp;
  80. my $path = "$examples_dir/$category/$example/$_";
  81. my $fname = $_;
  82. $fname =~ s/\A.*\///;
  83. $datafiles_str .= "$spc--embed-file=$path\@/$fname";
  84. $spc = ' ';
  85. }
  86. close($datafilesh);
  87. }
  88. my $dst = "$output_dir/$category/$example";
  89. print("Building $category/$example ...\n");
  90. do_mkdir($dst);
  91. # !!! FIXME: hardcoded SDL3 references, need to fix this for satellite libraries and SDL2.
  92. do_system("EMSDK_QUIET=1 source '$emsdk_dir/emsdk_env.sh' && emcc -s USE_SDL=0 -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s MAXIMUM_MEMORY=1gb -s ASSERTIONS=0 -o '$dst/index.js' '-I$examples_dir/../include' $files_str '$compile_dir/libSDL3.a' $datafiles_str") == 0
  93. or die("Failed to build $category/$example!\n");
  94. my $highlight_cmd = "highlight '--outdir=$dst' --style-outfile=highlight.css --fragment --enclose-pre --stdout --syntax=c '--plug-in=$examples_dir/highlight-plugin.lua'";
  95. print("$highlight_cmd\n");
  96. my $pid = open2(my $child_out, my $child_in, $highlight_cmd);
  97. my $htmlified_source_code = '';
  98. foreach (@files) {
  99. my $path = $_;
  100. open my $srccode, '<', $path or die("Couldn't open '$path': $!\n");
  101. my $fname = "$path";
  102. $fname =~ s/\A.*\///;
  103. print $child_in "/* $fname ... */\n\n";
  104. while (<$srccode>) {
  105. print $child_in $_;
  106. }
  107. close($srccode);
  108. }
  109. close($child_in);
  110. while (<$child_out>) {
  111. $htmlified_source_code .= $_;
  112. }
  113. close($child_out);
  114. waitpid($pid, 0);
  115. my $html = '';
  116. open my $htmltemplate, '<', "$examples_dir/template.html" or die("Couldn't open '$examples_dir/template.html': $!\n");
  117. while (<$htmltemplate>) {
  118. s/\@project_name\@/$project/g;
  119. s/\@category_name\@/$category/g;
  120. s/\@example_name\@/$example/g;
  121. s/\@htmlified_source_code\@/$htmlified_source_code/g;
  122. $html .= $_;
  123. }
  124. close($htmltemplate);
  125. open my $htmloutput, '>', "$dst/index.html" or die("Couldn't open '$dst/index.html': $!\n");
  126. print $htmloutput $html;
  127. close($htmloutput);
  128. }
  129. sub handle_category_dir {
  130. my $category = shift;
  131. print("Category $category ...\n");
  132. do_mkdir("$output_dir/$category");
  133. opendir(my $dh, "$examples_dir/$category") or die("Couldn't opendir '$examples_dir/$category': $!\n");
  134. while (readdir($dh)) {
  135. next if ($_ eq '.') || ($_ eq '..'); # obviously skip current and parent entries.
  136. next if not -d "$examples_dir/$category/$_"; # only care about subdirectories.
  137. handle_example_dir($category, $_);
  138. }
  139. closedir($dh);
  140. }
  141. # Mainline!
  142. foreach (@ARGV) {
  143. $project = $_, next if not defined $project;
  144. $emsdk_dir = $_, next if not defined $emsdk_dir;
  145. $compile_dir = $_, next if not defined $compile_dir;
  146. $output_dir = $_, next if not defined $output_dir;
  147. usage(); # too many arguments.
  148. }
  149. usage() if not defined $output_dir;
  150. print("Examples dir: $examples_dir\n");
  151. print("emsdk dir: $emsdk_dir\n");
  152. print("Compile dir: $compile_dir\n");
  153. print("Output dir: $output_dir\n");
  154. do_mkdir($output_dir);
  155. build_latest();
  156. opendir(my $dh, $examples_dir) or die("Couldn't opendir '$examples_dir': $!\n");
  157. while (readdir($dh)) {
  158. next if ($_ eq '.') || ($_ eq '..'); # obviously skip current and parent entries.
  159. next if not -d "$examples_dir/$_"; # only care about subdirectories.
  160. handle_category_dir($_);
  161. }
  162. closedir($dh);
  163. exit(0); # success!