Advertisement
cgrunwald

Untitled

Sep 14th, 2010
174
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 12.92 KB | None | 0 0
  1. #!/usr/bin/perl -w
  2. #!perl.exe -w
  3. ##-----------------------------------#
  4. ##  JuntBuild                       #
  5. ##  Simple script to automate the   #
  6. ##  process of minifying/combining  #
  7. ##  multiple assets, organizing     #
  8. ##  the project tree, and           #
  9. ##  refactoring as necessary.       #
  10. ###-----------------------------------#
  11. ## Native Modules
  12. use File::Copy;
  13. use File::Find;
  14. ## Third Party Modules
  15. use XML::LibXML;
  16. use JavaScript::Minifier;
  17. use CSS::Minifier;
  18.  
  19. # <Print Functions>
  20. ## Prints error messages and exits.
  21. sub PrintError {
  22.     my($error, $errcode) = @_;
  23.     print "Error: $error\n\n";
  24.     PrintUsage();
  25.     exit($errcode);
  26. }
  27. ## Prints the script usage.
  28. sub PrintUsage {
  29.     print "Usage:\n$^X $0 [filename]\n\tfilename\tOptional argument for specifying the xml build file.\n\t\t\tIf none specified, will use build.xml\n";
  30. }
  31. # </Print Functions>
  32.  
  33. # <Compressors>
  34. ## Javascript Compressor
  35. sub JsMinCompress {
  36.     my $file = $_[0];
  37.     my $sRet = "";
  38.     if (-s $file) {
  39.         open(INFILE, '<' . $file);
  40.             $sRet = JavaScript::Minifier::minify(input => *INFILE);
  41.         close(INFILE);
  42.     }
  43.     return $sRet;
  44. }
  45. ## CSS Compressor
  46. sub CssMinCompress {
  47.     my $file = $_[0];
  48.     my $sRet = "";
  49.     if (-s $file) {
  50.         open(INFILE, '<' . $file);
  51.             $sRet = CSS::Minifier::minify(input => *INFILE);
  52.         close(INFILE);
  53.     }
  54.     return $sRet;
  55. }
  56. ## Png Compressor
  57. sub PngCrushCompress {
  58.     my($file,$output) = @_;
  59.     if (-s $file) {
  60.         if(-e $output) {
  61.             unlink($output);
  62.         }
  63.         my $prog = "utils\\pngcrush.exe -brute \"" . $file . "\" \"" . $output . "\"";
  64.         $prog = `$prog`;
  65.         if(!-e $output) {
  66.             copy($file, $output);
  67.         }
  68.     }
  69. }
  70. #</Compressors>
  71.  
  72. # Recursively create directories
  73. sub CreateFolders {
  74.     my $path = $_[0];
  75.     my @folders = split(m!/!, $path);
  76.     pop(@folders);
  77.     my $buffer = "";
  78.     my $iswitch = 0;
  79.    
  80.     foreach $folder (@folders) {
  81.         if($folder ne "") {
  82.             if ($iswitch == 0) {
  83.                 $buffer = $folder;
  84.                 $iswitch = 1;
  85.             } else {
  86.                 $buffer = $buffer . "/" . $folder;
  87.             }
  88.             if(!-d $buffer) {
  89.                 mkdir($buffer);
  90.             }
  91.         }
  92.     }
  93. }
  94.  
  95. # <Entry Handlers>
  96. ## <Group Handlers>
  97. ### Javascript Groups
  98. sub JavascriptGroup {
  99.     # Get arguments.
  100.     my ($root, $files_ref, $output, $compress) = @_;
  101.     my @files = @{$files_ref};
  102.     my $buffer = "/* Packaged by JuntBuild v1.0 */";
  103.    
  104.     # Print output file.
  105.     if ($output =~ m!^(?:.*?)[/\\]([^/:*?<>"|]*?\.?[a-z\-_]{0,8})$!i) {
  106.         my $tmpout = $1;
  107.         print "----------\n$tmpout\n----------\n";
  108.     }
  109.    
  110.     # Cycle through files.
  111.     foreach my $file (@files) {
  112.         my $nfile = $root . $file->getAttribute("path");
  113.         print "Compressing $nfile...\n";
  114.        
  115.         if ($compress eq "jsmin") {
  116.             # Compress with JSMin
  117.             $buffer = $buffer . "\n" . JsMinCompress($nfile);
  118.         } elsif ($compress eq "none") {
  119.             open(INFILE, '<' . $nfile) or PrintError("Could not open file $file.\n$!\n", 1);
  120.             my $sContents = do{local $/; <INFILE>};
  121.             close(INFILE);
  122.             $buffer = $buffer . "\n" . $sContents;
  123.         }
  124.     }
  125.     # Write output to file.
  126.     CreateFolders($output);
  127.     open(OUTFILE, '>' . $output);
  128.         print OUTFILE $buffer;
  129.     close(OUTFILE);
  130. }
  131. ### CSS Groups
  132. sub CssGroup {
  133.     # Get arguments.
  134.     my ($root, $files_ref, $output, $compress) = @_;
  135.     my @files = @{$files_ref};
  136.     my $buffer = "/* Packaged by JuntBuild v1.0 */";
  137.    
  138.     # Print output file.
  139.     if ($output =~ m!^(?:.*?)[/\\]([^/:*?<>"|]*?\.?[a-z\-_]{0,8})$!i) {
  140.         my $tmpout = $1;
  141.         print "----------\n$tmpout\n----------\n";
  142.     }
  143.    
  144.     # Cycle through files.
  145.     foreach my $file (@files) {
  146.         my $nfile = $root . $file->getAttribute("path");
  147.         print "Compressing $nfile...\n";
  148.        
  149.         if ($compress eq "cssmin") {
  150.             # Compress with JSMin
  151.             $buffer = $buffer . "\n" . CssMinCompress($nfile);
  152.         } elsif ($compress eq "none") {
  153.             open(INFILE, '<' . $nfile) or PrintError("Could not open file $file.\n$!\n", 1);
  154.             my $sContents = do{local $/; <INFILE>};
  155.             close(INFILE);
  156.             $buffer = $buffer . "\n" . $sContents;
  157.         }
  158.     }
  159.     # Write output to file.
  160.     CreateFolders($output);
  161.     open(OUTFILE, '>' . $output);
  162.         print OUTFILE $buffer;
  163.     close(OUTFILE);
  164. }
  165. ## </Group Handlers>
  166.  
  167. ## <Single Handlers>
  168. ### Javascript Single
  169. sub JavascriptSingle {
  170.     my($file, $output, $compress) = @_;
  171.     if ($compress eq "jsmin") {
  172.         # Compress with JSMin
  173.         CreateFolders($output);
  174.         open(OUTFILE, '>' . $output);
  175.             print OUTFILE JsMinCompress($file);
  176.         close(OUTFILE);
  177.     } elsif ($compress eq "none") {
  178.         if(-e $output) {
  179.             unlink($output);
  180.         }
  181.         copy($file, $output);
  182.     }
  183. }
  184. ### Css Single
  185. sub CssSingle {
  186.     my($file, $output, $compress) = @_;
  187.     CreateFolders($output);
  188.     if ($compress eq "cssmin") {
  189.         # Compress with JSMin
  190.         open(OUTFILE, '>' . $output);
  191.             print OUTFILE CssMinCompress($file);
  192.         close(OUTFILE);
  193.     } elsif ($compress eq "none") {
  194.         if(-e $output) {
  195.             unlink($output);
  196.         }
  197.         copy($file, $output);
  198.     }
  199. }
  200. ### Png Single
  201. sub PngSingle {
  202.     my($file, $output, $compress) = @_;
  203.     CreateFolders($output);
  204.     if($compress eq "pngcrush") {
  205.         # Compress with pngcrush.
  206.         PngCrushCompress($file,$output);
  207.     } elsif ($compress eq "none") {
  208.         if(-e $output) {
  209.             unlink($output);
  210.         }
  211.         copy($file, $output);
  212.     }
  213. }
  214. ### Generic File Single
  215. sub FileSingle {
  216.     my($file, $output, $compress) = @_;
  217.     CreateFolders($output);
  218.     if(-e $output) {
  219.         unlink($output);
  220.     }
  221.     copy($file, $output);
  222. }
  223. ## </Single Handlers>
  224.  
  225. ## <Container Handlers>
  226. ### Html Container
  227. sub HtmlContainer {
  228.     my($file, $output, $scripts_ref, $styles_ref) = @_;
  229.     my @styles = @{$styles_ref};
  230.     my @scripts = @{$scripts_ref};
  231.  
  232.     # Open the original file.
  233.     open(INFILE, '<' . $file) or PrintError("Could not open file $file.\n$!\n", 1);
  234.     my $sContents = do{local $/; <INFILE>};
  235.     close(INFILE);
  236.    
  237.     # Remove all script and style references.
  238.     $sContents =~ s!(<script.*?>.*?</script>)|(<link.*?type="text/css".*?>)!!img;
  239.    
  240.     # Cycle through styles
  241.     foreach my $style (@styles) {
  242.         my $style_path = $style->getAttribute("path");
  243.         $sContents =~ s!^(.*?)</head>(.*?)$!\t\t<link href=\"$style_path\" rel=\"stylesheet\" type=\"text/css\" />\n$1</head>$2!img;
  244.     }
  245.    
  246.     # Cycle through scripts
  247.     foreach my $script (@scripts) {
  248.         my $script_path = $script->getAttribute("path");
  249.         my $script_type = $script->getAttribute("type");
  250.         $sContents =~ s!^(.*?)</head>(.*?)$!\t\t<script type=\"$script_type\" src=\"$script_path\"></script>\n$1</head>$2!img;
  251.     }
  252.    
  253.     # Open the output file.
  254.     CreateFolders($output);
  255.     open(OUTFILE, '>' . $output) or PrintError("Could not open/create file $output.\n$!\n", 1);
  256.         print OUTFILE $sContents;
  257.     close(OUTFILE);
  258. }
  259. ## </Container Handlers>
  260.  
  261. ## <Refactor Handlers>
  262. ## Text Refactoring
  263. my $val_old; my $val_new; my $find_scope;
  264. sub textRefacFound {
  265.     $file = $_;
  266.     if(($File::Find::name =~ m/$find_scope/im)&&(!-d $File::Find::name)) {
  267.         print "Refactoring $_...\n";
  268.     } else {
  269.         return;
  270.     }
  271.  
  272.     # Open the original file.
  273.     open(INFILE, '<' . $file) or PrintError("Could not open file $file.\n$!\n", 1);
  274.     my $sContents = do{local $/; <INFILE>};
  275.     close(INFILE);
  276.     # Make changes.
  277.     $sContents =~ s/\Q$val_old\E/$val_new/img;
  278.     # Reopen and apply changes.
  279.     open(OUTFILE, '>' . $file) or PrintError("Could not open/create file $file.\n$!\n", 1);
  280.         print OUTFILE $sContents;
  281.     close(OUTFILE);
  282. }
  283. sub TextRefactor {
  284.     my($scope, $output, $old, $new) = @_;
  285.    
  286.     # Fix regex.
  287.     $scope = quotemeta($scope);
  288.     $charLast = substr $scope, -2, 2;
  289.     if($charLast ne "\\\$") {
  290.         $scope = $scope . "\$";
  291.     }
  292.     $charFirst = substr $scope, 0, 1;
  293.     if($charFirst ne "/") {
  294.         $scope = "/" . $scope;
  295.     }
  296.     # Check for wildcard.
  297.     $scope =~ s/\\\*/[^\/:*?<>"|]*?/img;
  298.    
  299.     # Set the global variables.
  300.     $val_old = $old; $val_new = $new;
  301.     $find_scope = $scope;
  302.    
  303.     # Search
  304.     find(\&textRefacFound, $output);
  305.  
  306. }
  307.  
  308. ##</Refactor Handlers>
  309. # </Entry Handlers>
  310.  
  311. # Script Entry Point.
  312. # Header
  313. print "=============\n+ JuntBuild\n- Version 1.0\n=============\n";
  314.  
  315. # Argument handling
  316. $numArgs = $#ARGV + 1;
  317. $sFile = "build.xml";
  318. if ($numArgs == 0) {
  319.     if (!(-s $sFile)||!(-r $sFile)) {
  320.         PrintError("File $sFile did not exist or was empty.", 1);
  321.     }
  322. } elsif ($numArgs == 1) {
  323.     if ((-s $ARGV[0])&&(-r $ARGV[0])) {
  324.         $sFile = $ARGV[0];
  325.     } else {
  326.         $sFile = $ARGV[0];
  327.         PrintError("File $sFile did not exist or was empty.", 1);
  328.     }
  329. }
  330.  
  331. # Parse xml document.
  332. print "Parsing $sFile..\n";
  333. my $xmlParser = XML::LibXML->new();
  334. my $xmlTree = $xmlParser->parse_file($sFile);
  335. my $xmlRoot = $xmlTree->getDocumentElement;
  336.  
  337. # Get output path.
  338. @BuildConfig = $xmlRoot->getElementsByTagName("buildconfig");
  339. @pathRoot = $BuildConfig[0]->getElementsByTagName("root");
  340. $pathRoot = $pathRoot[0]->getAttribute("path");
  341. @pathOut = $BuildConfig[0]->getElementsByTagName("output");
  342. $pathOut = $pathOut[0]->getAttribute("path");
  343.  
  344. # Fix output path
  345. $charLast = substr $pathOut, -1, 1;
  346. if($charLast eq "/") {
  347.     chop($pathOut);
  348. }
  349.  
  350. # Create output directory if it doesn't exist.
  351. if(!-d $pathOut) {
  352.     # Directory does not exist.
  353.     print "Creating $pathOut..";
  354.     mkdir($pathOut) or PrintError("Could not create output folder.\n$!", 3);
  355. }
  356.  
  357. # Add / to output path
  358. $pathOut = $pathOut . "/";
  359.  
  360. # Actions to do beforehand.
  361. print "\n==============================\nProcessing Pre-Build Actions..\n==============================\n";
  362. my @Pres = $xmlRoot->getElementsByTagName("pre-actions");
  363. if (@Pres) {
  364.     @Pres = $Pres[0]->getElementsByTagName("pre");
  365.     foreach $pre (@Pres) {
  366.         my $action = $pre->getAttribute("action");
  367.         print "Processing pre-action: $action\n";
  368.         $trash = `$action`;
  369.     }
  370. }
  371.  
  372. # Cycle through groups.
  373. print "\n===================\nProcessing Groups..\n===================\n";
  374. my @BuildGroups = $xmlRoot->getElementsByTagName("buildgroups");
  375. @BuildGroups = $BuildGroups[0]->getElementsByTagName("group");
  376. foreach $group (@BuildGroups) {
  377.     # Get output path.
  378.     my @buildOut = $group->getElementsByTagName("output");
  379.     $buildOut = $buildOut[0]->getAttribute("path");
  380.     $buildOut = $pathOut . $buildOut;
  381.    
  382.     # Group files
  383.     my @files = $group->getElementsByTagName("files");
  384.     @files = $files[0]->getElementsByTagName("file");
  385.    
  386.     # Compression method
  387.     my $buildCompress = $group->getAttribute("compress");
  388.    
  389.    
  390.     # Work on type.
  391.     my $attr = $group->getAttribute("type");
  392.     if ($attr eq "js") {
  393.         # Javascript Group
  394.         JavascriptGroup($pathRoot, \@files, $buildOut, $buildCompress);
  395.     } elsif ($attr eq "css") {
  396.         # CSS Group
  397.         CssGroup($pathRoot, \@files, $buildOut, $buildCompress);
  398.     }
  399. }
  400.  
  401. # Cycle through singles.
  402. print "\n====================\nProcessing Singles..\n====================\n";
  403. my @Singles = $xmlRoot->getElementsByTagName("singles");
  404. @Singles = $Singles[0]->getElementsByTagName("single");
  405. foreach $single (@Singles) {
  406.     # Get output path.
  407.     my @buildOut = $single->getElementsByTagName("output");
  408.     $buildOut = $buildOut[0]->getAttribute("path");
  409.     print "Generating $buildOut..\n";
  410.     $buildOut = $pathOut . $buildOut;
  411.    
  412.     # Single file
  413.     my @file = $single->getElementsByTagName("file");
  414.     $file = $pathRoot . $file[0]->getAttribute("path");
  415.    
  416.     # Compression method
  417.     my $buildCompress = $single->getAttribute("compress");
  418.    
  419.     # Work on type.
  420.     my $attr = $single->getAttribute("type");
  421.     if ($attr eq "js") {
  422.         # Javascript Single
  423.         JavascriptSingle($file, $buildOut, $buildCompress);
  424.     } elsif ($attr eq "css") {
  425.         # CSS Single
  426.         CssSingle($file, $buildOut, $buildCompress);
  427.     } elsif ($attr eq "png") {
  428.         # CSS Single
  429.         PngSingle($file, $buildOut, $buildCompress);
  430.     } elsif ($attr eq "file") {
  431.         # CSS Single
  432.         FileSingle($file, $buildOut, $buildCompress);
  433.     }
  434. }
  435.  
  436. # Cycle through containers.
  437. print "\n======================\nProcessing Containers..\n======================\n";
  438. my @Containers = $xmlRoot->getElementsByTagName("containers");
  439. @Containers = $Containers[0]->getElementsByTagName("container");
  440. foreach $container (@Containers) {
  441.     # Get output path.
  442.     my @buildOut = $container->getElementsByTagName("output");
  443.     $buildOut = $buildOut[0]->getAttribute("path");
  444.     print "Generating container: $buildOut..\n";
  445.     $buildOut = $pathOut . $buildOut;
  446.    
  447.     # Single file
  448.     my $file = $pathRoot . $container->getAttribute("file");
  449.    
  450.     # Stylesheets
  451.     my @stylesheets = $container->getElementsByTagName("stylesheets");
  452.     @stylesheets = $stylesheets[0]->getElementsByTagName("stylesheet");
  453.    
  454.     # Scripts
  455.     my @scripts = $container->getElementsByTagName("scripts");
  456.     @scripts = $scripts[0]->getElementsByTagName("script");
  457.    
  458.     # Work on type.
  459.     my $attr = $container->getAttribute("type");
  460.     if ($attr eq "html") {
  461.         # Html Container
  462.         HtmlContainer($file, $buildOut, \@scripts, \@stylesheets);
  463.     }
  464. }
  465.  
  466. # Cycle through containers.
  467. print "\n======================\nProcessing Refactors..\n======================\n";
  468. my @Refactors = $xmlRoot->getElementsByTagName("refactors");
  469. @Refactors = $Refactors[0]->getElementsByTagName("refactor");
  470. foreach $refactor (@Refactors) {
  471.     # Single file
  472.     my $scope = $refactor->getAttribute("scope");
  473.    
  474.     # Get output path.
  475.     @Values = $refactor->getElementsByTagName("value");
  476.     my $valOld = $Values[0]->getAttribute("old");
  477.     my $valNew = $Values[0]->getAttribute("new");
  478.    
  479.     # Work on type.
  480.     my $attr = $refactor->getAttribute("type");
  481.     if ($attr eq "text") {
  482.         # Text refactoring
  483.         TextRefactor($scope, $pathOut, $valOld, $valNew);
  484.     }
  485. }
  486.  
  487. # Actions to do afterwards.
  488. print "\n===============================\nProcessing Post-Build Actions..\n===============================\n";
  489. my @Posts = $xmlRoot->getElementsByTagName("post-actions");
  490. if (@Posts) {
  491.     @Posts = $Posts[0]->getElementsByTagName("post");
  492.     foreach $post (@Posts) {
  493.         my $action = $post->getAttribute("action");
  494.         print "Processing post-action: $action\n";
  495.         $trash = `$action`;
  496.     }
  497. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement