Simple Directmedia Layer
at main 14 kB view raw
1#!/usr/bin/perl -w 2 3# Simple DirectMedia Layer 4# Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org> 5# 6# This software is provided 'as-is', without any express or implied 7# warranty. In no event will the authors be held liable for any damages 8# arising from the use of this software. 9# 10# Permission is granted to anyone to use this software for any purpose, 11# including commercial applications, and to alter it and redistribute it 12# freely, subject to the following restrictions: 13# 14# 1. The origin of this software must not be misrepresented; you must not 15# claim that you wrote the original software. If you use this software 16# in a product, an acknowledgment in the product documentation would be 17# appreciated but is not required. 18# 2. Altered source versions must be plainly marked as such, and must not be 19# misrepresented as being the original software. 20# 3. This notice may not be removed or altered from any source distribution. 21 22use warnings; 23use strict; 24use File::Basename; 25use File::Copy; 26use Cwd qw(abs_path); 27use IPC::Open2; 28 29my $examples_dir = abs_path(dirname(__FILE__) . "/../examples"); 30my $project = undef; 31my $emsdk_dir = undef; 32my $compile_dir = undef; 33my $cmake_flags = undef; 34my $output_dir = undef; 35 36sub usage { 37 die("USAGE: $0 <project_name> <emsdk_dir> <compiler_output_directory> <cmake_flags> <html_output_directory>\n\n"); 38} 39 40sub do_system { 41 my $cmd = shift; 42 $cmd = "exec /bin/bash -c \"$cmd\""; 43 print("$cmd\n"); 44 return system($cmd); 45} 46 47sub do_mkdir { 48 my $d = shift; 49 if ( ! -d $d ) { 50 print("mkdir '$d'\n"); 51 mkdir($d) or die("Couldn't mkdir('$d'): $!\n"); 52 } 53} 54 55sub do_copy { 56 my $src = shift; 57 my $dst = shift; 58 print("cp '$src' '$dst'\n"); 59 copy($src, $dst) or die("Failed to copy '$src' to '$dst': $!\n"); 60} 61 62sub build_latest { 63 # Try to build just the latest without re-running cmake, since that is SLOW. 64 print("Building latest version of $project ...\n"); 65 if (do_system("EMSDK_QUIET=1 source '$emsdk_dir/emsdk_env.sh' && cd '$compile_dir' && ninja") != 0) { 66 # Build failed? Try nuking the build dir and running CMake from scratch. 67 print("\n\nBuilding latest version of $project FROM SCRATCH ...\n"); 68 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 $cmake_flags '$examples_dir/..' && ninja") != 0) { 69 die("Failed to build latest version of $project!\n"); # oh well. 70 } 71 } 72} 73 74sub get_category_description { 75 my $category = shift; 76 my $retval = ucfirst($category); 77 78 if (open(my $fh, '<', "$examples_dir/$category/description.txt")) { 79 $retval = <$fh>; 80 chomp($retval); 81 close($fh); 82 } 83 84 return $retval; 85} 86 87sub get_categories { 88 my @categories = (); 89 90 if (open(my $fh, '<', "$examples_dir/categories.txt")) { 91 while (<$fh>) { 92 chomp; 93 s/\A\s+//; 94 s/\s+\Z//; 95 next if $_ eq ''; 96 next if /\A\#/; 97 push @categories, $_; 98 } 99 close($fh); 100 } else { 101 opendir(my $dh, $examples_dir) or die("Couldn't opendir '$examples_dir': $!\n"); 102 foreach my $dir (sort readdir $dh) { 103 next if ($dir eq '.') || ($dir eq '..'); # obviously skip current and parent entries. 104 next if not -d "$examples_dir/$dir"; # only care about subdirectories. 105 push @categories, $dir; 106 } 107 closedir($dh); 108 } 109 110 return @categories; 111} 112 113sub get_examples_for_category { 114 my $category = shift; 115 116 my @examples = (); 117 118 opendir(my $dh, "$examples_dir/$category") or die("Couldn't opendir '$examples_dir/$category': $!\n"); 119 foreach my $dir (sort readdir $dh) { 120 next if ($dir eq '.') || ($dir eq '..'); # obviously skip current and parent entries. 121 next if not -d "$examples_dir/$category/$dir"; # only care about subdirectories. 122 123 push @examples, $dir; 124 } 125 closedir($dh); 126 127 return @examples; 128} 129 130sub handle_example_dir { 131 my $category = shift; 132 my $example = shift; 133 134 my @files = (); 135 my $files_str = ''; 136 opendir(my $dh, "$examples_dir/$category/$example") or die("Couldn't opendir '$examples_dir/$category/$example': $!\n"); 137 my $spc = ''; 138 while (readdir($dh)) { 139 my $path = "$examples_dir/$category/$example/$_"; 140 next if not -f $path; # only care about files. 141 push @files, $path if /\.[ch]\Z/; # add .c and .h files to source code. 142 if (/\.c\Z/) { # only care about .c files for compiling. 143 $files_str .= "$spc$path"; 144 $spc = ' '; 145 } 146 } 147 closedir($dh); 148 149 my $dst = "$output_dir/$category/$example"; 150 151 print("Building $category/$example ...\n"); 152 153 my $basefname = "$example"; 154 $basefname =~ s/\A\d+\-//; 155 $basefname = "$category-$basefname"; 156 my $jsfname = "$basefname.js"; 157 my $wasmfname = "$basefname.wasm"; 158 my $thumbnailfname = 'thumbnail.png'; 159 my $onmouseoverfname = 'onmouseover.webp'; 160 my $jssrc = "$compile_dir/examples/$jsfname"; 161 my $wasmsrc = "$compile_dir/examples/$wasmfname"; 162 my $thumbnailsrc = "$examples_dir/$category/$example/$thumbnailfname"; 163 my $onmouseoversrc = "$examples_dir/$category/$example/$onmouseoverfname"; 164 my $jsdst = "$dst/$jsfname"; 165 my $wasmdst = "$dst/$wasmfname"; 166 my $thumbnaildst = "$dst/$thumbnailfname"; 167 my $onmouseoverdst = "$dst/$onmouseoverfname"; 168 169 my $description = ''; 170 my $has_paragraph = 0; 171 if (open(my $readmetxth, '<', "$examples_dir/$category/$example/README.txt")) { 172 while (<$readmetxth>) { 173 chomp; 174 s/\A\s+//; 175 s/\s+\Z//; 176 if (($_ eq '') && ($description ne '')) { 177 $has_paragraph = 1; 178 } else { 179 if ($has_paragraph) { 180 $description .= "\n<br/>\n<br/>\n"; 181 $has_paragraph = 0; 182 } 183 $description .= "$_ "; 184 } 185 } 186 close($readmetxth); 187 $description =~ s/\s+\Z//; 188 } 189 190 do_mkdir($dst); 191 do_copy($jssrc, $jsdst); 192 do_copy($wasmsrc, $wasmdst); 193 do_copy($thumbnailsrc, $thumbnaildst) if ( -f $thumbnailsrc ); 194 do_copy($onmouseoversrc, $onmouseoverdst) if ( -f $onmouseoversrc ); 195 196 my $highlight_cmd = "highlight '--outdir=$dst' --style-outfile=highlight.css --fragment --enclose-pre --stdout --syntax=c '--plug-in=$examples_dir/highlight-plugin.lua'"; 197 print("$highlight_cmd\n"); 198 my $pid = open2(my $child_out, my $child_in, $highlight_cmd); 199 200 my $htmlified_source_code = ''; 201 foreach (sort(@files)) { 202 my $path = $_; 203 open my $srccode, '<', $path or die("Couldn't open '$path': $!\n"); 204 my $fname = "$path"; 205 $fname =~ s/\A.*\///; 206 print $child_in "/* $fname ... */\n\n"; 207 while (<$srccode>) { 208 print $child_in $_; 209 } 210 print $child_in "\n\n\n"; 211 close($srccode); 212 } 213 214 close($child_in); 215 216 while (<$child_out>) { 217 $htmlified_source_code .= $_; 218 } 219 close($child_out); 220 221 waitpid($pid, 0); 222 223 my $other_examples_html = "<ul>"; 224 foreach my $example (get_examples_for_category($category)) { 225 $other_examples_html .= "<li><a href='/$project/$category/$example'>$category/$example</a></li>"; 226 } 227 $other_examples_html .= "</ul>"; 228 229 my $category_description = get_category_description($category); 230 my $preview_image = get_example_thumbnail($project, $category, $example); 231 232 my $html = ''; 233 open my $htmltemplate, '<', "$examples_dir/template.html" or die("Couldn't open '$examples_dir/template.html': $!\n"); 234 while (<$htmltemplate>) { 235 s/\@project_name\@/$project/g; 236 s/\@category_name\@/$category/g; 237 s/\@category_description\@/$category_description/g; 238 s/\@example_name\@/$example/g; 239 s/\@javascript_file\@/$jsfname/g; 240 s/\@htmlified_source_code\@/$htmlified_source_code/g; 241 s/\@description\@/$description/g; 242 s/\@preview_image\@/$preview_image/g; 243 s/\@other_examples_html\@/$other_examples_html/g; 244 $html .= $_; 245 } 246 close($htmltemplate); 247 248 open my $htmloutput, '>', "$dst/index.html" or die("Couldn't open '$dst/index.html': $!\n"); 249 print $htmloutput $html; 250 close($htmloutput); 251} 252 253sub get_example_thumbnail { 254 my $project = shift; 255 my $category = shift; 256 my $example = shift; 257 258 if ( -f "$examples_dir/$category/$example/thumbnail.png" ) { 259 return "/$project/$category/$example/thumbnail.png"; 260 } elsif ( -f "$examples_dir/$category/thumbnail.png" ) { 261 return "/$project/$category/thumbnail.png"; 262 } 263 264 return "/$project/thumbnail.png"; 265} 266 267sub generate_example_thumbnail { 268 my $project = shift; 269 my $category = shift; 270 my $example = shift; 271 272 my $example_no_num = "$example"; 273 $example_no_num =~ s/\A\d+\-//; 274 275 my $example_image_url = get_example_thumbnail($project, $category, $example); 276 277 my $example_mouseover_html = ''; 278 if ( -f "$examples_dir/$category/$example/onmouseover.webp" ) { 279 $example_mouseover_html = "onmouseover=\"this.src='/$project/$category/$example/onmouseover.webp'\" onmouseout=\"this.src='$example_image_url';\""; 280 } elsif ( -f "$examples_dir/$category/onmouseover.webp" ) { 281 $example_mouseover_html = "onmouseover=\"this.src='/$project/$category/onmouseover.webp'\" onmouseout=\"this.src='$example_image_url';\""; 282 } 283 284 return " 285 <a href='/$project/$category/$example'> 286 <div> 287 <img src='$example_image_url' $example_mouseover_html /> 288 <div>$example_no_num</div> 289 </div> 290 </a>" 291 ; 292} 293 294sub generate_example_thumbnails_for_category { 295 my $project = shift; 296 my $category = shift; 297 my @examples = get_examples_for_category($category); 298 my $retval = ''; 299 foreach my $example (@examples) { 300 $retval .= generate_example_thumbnail($project, $category, $example); 301 } 302 return $retval; 303} 304 305sub handle_category_dir { 306 my $category = shift; 307 308 print("Category $category ...\n"); 309 310 do_mkdir("$output_dir/$category"); 311 312 opendir(my $dh, "$examples_dir/$category") or die("Couldn't opendir '$examples_dir/$category': $!\n"); 313 314 while (readdir($dh)) { 315 next if ($_ eq '.') || ($_ eq '..'); # obviously skip current and parent entries. 316 next if not -d "$examples_dir/$category/$_"; # only care about subdirectories. 317 handle_example_dir($category, $_); 318 } 319 320 closedir($dh); 321 322 my $examples_list_html = generate_example_thumbnails_for_category($project, $category); 323 324 my $dst = "$output_dir/$category"; 325 326 do_copy("$examples_dir/$category/thumbnail.png", "$dst/thumbnail.png") if ( -f "$examples_dir/$category/thumbnail.png" ); 327 do_copy("$examples_dir/$category/onmouseover.webp", "$dst/onmouseover.webp") if ( -f "$examples_dir/$category/onmouseover.webp" ); 328 329 my $category_description = get_category_description($category); 330 my $preview_image = "/$project/thumbnail.png"; 331 if ( -f "$examples_dir/$category/thumbnail.png" ) { 332 $preview_image = "/$project/$category/thumbnail.png"; 333 } 334 335 # write category page 336 my $html = ''; 337 open my $htmltemplate, '<', "$examples_dir/template-category.html" or die("Couldn't open '$examples_dir/template-category.html': $!\n"); 338 while (<$htmltemplate>) { 339 s/\@project_name\@/$project/g; 340 s/\@category_name\@/$category/g; 341 s/\@category_description\@/$category_description/g; 342 s/\@examples_list_html\@/$examples_list_html/g; 343 s/\@preview_image\@/$preview_image/g; 344 $html .= $_; 345 } 346 close($htmltemplate); 347 348 open my $htmloutput, '>', "$dst/index.html" or die("Couldn't open '$dst/index.html': $!\n"); 349 print $htmloutput $html; 350 close($htmloutput); 351} 352 353 354# Mainline! 355 356foreach (@ARGV) { 357 $project = $_, next if not defined $project; 358 $emsdk_dir = $_, next if not defined $emsdk_dir; 359 $compile_dir = $_, next if not defined $compile_dir; 360 $cmake_flags = $_, next if not defined $cmake_flags; 361 $output_dir = $_, next if not defined $output_dir; 362 usage(); # too many arguments. 363} 364 365usage() if not defined $output_dir; 366 367print("Examples dir: $examples_dir\n"); 368print("emsdk dir: $emsdk_dir\n"); 369print("Compile dir: $compile_dir\n"); 370print("CMake flags: $cmake_flags\n"); 371print("Output dir: $output_dir\n"); 372 373do_system("rm -rf '$output_dir'"); 374do_mkdir($output_dir); 375 376build_latest(); 377 378do_copy("$examples_dir/template.css", "$output_dir/examples.css"); 379do_copy("$examples_dir/template-placeholder.png", "$output_dir/thumbnail.png"); 380 381opendir(my $dh, $examples_dir) or die("Couldn't opendir '$examples_dir': $!\n"); 382 383while (readdir($dh)) { 384 next if ($_ eq '.') || ($_ eq '..'); # obviously skip current and parent entries. 385 next if not -d "$examples_dir/$_"; # only care about subdirectories. 386 # !!! FIXME: this needs to generate a preview page for all the categories. 387 handle_category_dir($_); 388} 389 390closedir($dh); 391 392# write homepage 393my $homepage_list_html = ""; 394foreach my $category (get_categories()) { 395 my $category_description = get_category_description($category); 396 $homepage_list_html .= "<h2>$category_description</h2>"; 397 $homepage_list_html .= "<div class='list'>"; 398 $homepage_list_html .= generate_example_thumbnails_for_category($project, $category); 399 $homepage_list_html .= "</div>"; 400} 401 402my $preview_image = "/$project/thumbnail.png"; 403 404my $dst = "$output_dir/"; 405my $html = ''; 406open my $htmltemplate, '<', "$examples_dir/template-homepage.html" or die("Couldn't open '$examples_dir/template-category.html': $!\n"); 407while (<$htmltemplate>) { 408 s/\@project_name\@/$project/g; 409 s/\@homepage_list_html\@/$homepage_list_html/g; 410 s/\@preview_image\@/$preview_image/g; 411 $html .= $_; 412} 413close($htmltemplate); 414 415open my $htmloutput, '>', "$dst/index.html" or die("Couldn't open '$dst/index.html': $!\n"); 416print $htmloutput $html; 417close($htmloutput); 418 419print("All examples built successfully!\n"); 420exit(0); # success!