Replace the specialized Python script used for it before (and which was actually forgotten to run a couple of times already) with a newer version of upmake, which can now update CMake variable definitions too.
		
			
				
	
	
		
			1521 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1521 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env perl
 | |
| 
 | |
| # This chunk of stuff was generated by App::FatPacker. To find the original
 | |
| # file's code, look for the end of this BEGIN block or the string 'FATPACK'
 | |
| BEGIN {
 | |
| my %fatpacked;
 | |
| 
 | |
| $fatpacked{"Makefile/Update.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'MAKEFILE_UPDATE';
 | |
|   package Makefile::Update;
 | |
|   
 | |
|   # ABSTRACT: Update make files.
 | |
|   
 | |
|   use strict;
 | |
|   use warnings;
 | |
|   use autodie;
 | |
|   
 | |
|   use Exporter qw(import);
 | |
|   
 | |
|   our @EXPORT = qw(read_files_list upmake);
 | |
|   
 | |
|   # VERSION
 | |
|   
 | |
|   =head1 SYNOPSIS
 | |
|   
 | |
|       use Makefile::Update;
 | |
|       my $vars = read_files_list('files.lst');
 | |
|       upmake('foo.vcxproj', $vars->{sources}, $vars->{headers});
 | |
|   
 | |
|   =cut
 | |
|   
 | |
|   =func read_files_list
 | |
|   
 | |
|   Reads the file containing the file lists definitions and returns a hash ref
 | |
|   with variable names as keys and refs to arrays of the file names as values.
 | |
|   
 | |
|   Takes an (open) file handle as argument.
 | |
|   
 | |
|   The file contents is supposed to have the following very simple format:
 | |
|   
 | |
|       # Comments are allowed and ignored.
 | |
|       #
 | |
|       # The variable definitions must always be in the format shown below,
 | |
|       # i.e. whitespace is significant and there should always be a single
 | |
|       # file per line.
 | |
|       sources =
 | |
|           file1.cpp
 | |
|           file2.cpp
 | |
|   
 | |
|       headers =
 | |
|           file1.h
 | |
|           file2.h
 | |
|   
 | |
|       # It is also possible to define variables in terms of other variables
 | |
|       # defined before it in the file (no forward references):
 | |
|       everything =
 | |
|           $sources
 | |
|           $headers
 | |
|   =cut
 | |
|   
 | |
|   sub read_files_list
 | |
|   {
 | |
|       my ($fh) = @_;
 | |
|   
 | |
|       my ($var, %vars);
 | |
|       while (<$fh>) {
 | |
|           chomp;
 | |
|           s/#.*$//;
 | |
|           s/^\s+//;
 | |
|           s/\s+$//;
 | |
|           next if !$_;
 | |
|   
 | |
|           if (/^(\w+)\s*=$/) {
 | |
|               $var = $1;
 | |
|           } else {
 | |
|               die "Unexpected contents outside variable definition at line $.\n"
 | |
|                   unless defined $var;
 | |
|               if (/^\$(\w+)$/) {
 | |
|                   my $name = $1;
 | |
|                   die qq{Reference to undefined variable "$name" in the } .
 | |
|                       qq{assignment to "$var" at line $.\n}
 | |
|                       unless exists $vars{$name};
 | |
|                   my $value = $vars{$name};
 | |
|                   push @{$vars{$var}}, $_ for @$value;
 | |
|               } else {
 | |
|                   push @{$vars{$var}}, $_;
 | |
|               }
 | |
|           }
 | |
|       }
 | |
|   
 | |
|       return \%vars;
 | |
|   }
 | |
|   
 | |
|   =func upmake
 | |
|   
 | |
|   Update a file in place using the specified function and passing it the rest of
 | |
|   the arguments.
 | |
|   
 | |
|   The first parameter is either just the file path or a hash reference which may
 | |
|   contain the following keys:
 | |
|   
 | |
|   =over
 | |
|   
 | |
|   =item C<file>
 | |
|   
 | |
|   The path to the file to be updated, required.
 | |
|   
 | |
|   =item C<verbose>
 | |
|   
 | |
|   If true, give more messages about what is being done.
 | |
|   
 | |
|   =item C<quiet>
 | |
|   
 | |
|   If true, don't output any non-error messages.
 | |
|   
 | |
|   =item C<dryrun>
 | |
|   
 | |
|   If true, don't really update the file but just output whether it would have
 | |
|   been updated or not. If C<verbose> is also true, also output the diff of the
 | |
|   changes that would have been done.
 | |
|   
 | |
|   =back
 | |
|   
 | |
|   This is meant to be used with C<update_xxx()> defined in different
 | |
|   Makefile::Update::Xxx modules.
 | |
|   
 | |
|   Returns 1 if the file was changed or 0 otherwise.
 | |
|   =cut
 | |
|   
 | |
|   sub upmake
 | |
|   {
 | |
|       my $file_or_options = shift;
 | |
|       my ($updater, @args) = @_;
 | |
|   
 | |
|       my ($fname, $verbose, $quiet, $dryrun);
 | |
|       if (ref $file_or_options eq 'HASH') {
 | |
|           $fname = $file_or_options->{file};
 | |
|           $verbose = $file_or_options->{verbose};
 | |
|           $quiet = $file_or_options->{quiet};
 | |
|           $dryrun = $file_or_options->{dryrun};
 | |
|       } else {
 | |
|           $fname = $file_or_options;
 | |
|           $verbose =
 | |
|           $quiet =
 | |
|           $dryrun = 0;
 | |
|       }
 | |
|   
 | |
|       if ($dryrun) {
 | |
|           my $old = do {
 | |
|               local $/;
 | |
|               open my $f, '<', $fname;
 | |
|               <$f>
 | |
|           };
 | |
|           my $new = '';
 | |
|   
 | |
|           open my $in, '<', \$old;
 | |
|           open my $out, '>', \$new;
 | |
|   
 | |
|           if ($updater->($in, $out, @args)) {
 | |
|               print qq{Would update "$fname"};
 | |
|   
 | |
|               if ($verbose) {
 | |
|                   if (eval { require Text::Diff; }) {
 | |
|                       print " with the following changes:\n";
 | |
|   
 | |
|                       print Text::Diff::diff(\$old, \$new, {
 | |
|                                   FILENAME_A => $fname,
 | |
|                                   FILENAME_B => "$fname.new"
 | |
|                               });
 | |
|                   } else {
 | |
|                       print ".\n";
 | |
|   
 | |
|                       warn qq{Can't display diff of the changes, please install Text::Diff module.\n};
 | |
|                   }
 | |
|               } else {
 | |
|                   print ".\n";
 | |
|               }
 | |
|           } else {
 | |
|               print qq{Wouldn't change the file "$fname".\n};
 | |
|           }
 | |
|   
 | |
|           return 0;
 | |
|       }
 | |
|   
 | |
|       my $fname_new = "$fname.upmake.new"; # TODO make it more unique
 | |
|   
 | |
|       open my $in, '<', $fname;
 | |
|       open my $out, '>', $fname_new;
 | |
|   
 | |
|       my $changed = $updater->($in, $out, @args);
 | |
|   
 | |
|       close $in;
 | |
|       close $out;
 | |
|   
 | |
|       if ($changed) {
 | |
|           rename $fname_new, $fname;
 | |
|       } else {
 | |
|           unlink $fname_new;
 | |
|       }
 | |
|   
 | |
|       if ($changed) {
 | |
|           print qq{File "$fname" successfully updated.\n} unless $quiet;
 | |
|           return 1;
 | |
|       } else {
 | |
|           print qq{No changes in the file "$fname".\n} if $verbose;
 | |
|           return 0;
 | |
|       }
 | |
|   }
 | |
|   
 | |
|   1;
 | |
| MAKEFILE_UPDATE
 | |
| 
 | |
| $fatpacked{"Makefile/Update/Bakefile0.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'MAKEFILE_UPDATE_BAKEFILE0';
 | |
|   package Makefile::Update::Bakefile0;
 | |
|   # ABSTRACT: Update bakefile-0.x files list.
 | |
|   
 | |
|   use Exporter qw(import);
 | |
|   our @EXPORT = qw(update_bakefile_0);
 | |
|   
 | |
|   use strict;
 | |
|   use warnings;
 | |
|   
 | |
|   # VERSION
 | |
|   
 | |
|   =head1 SYNOPSIS
 | |
|   
 | |
|   This is used exclusively to update wxWidgets C<files.bkl> and is probably not
 | |
|   useful outside of wxWidgets project.
 | |
|   
 | |
|       use Makefile::Update::Bakefile0;
 | |
|       Makefile::Update::upmake('bakefiles/files.bkl', \&update_bakefile_0, $vars);
 | |
|   
 | |
|   =head1 SEE ALSO
 | |
|   
 | |
|   Makefile::Update
 | |
|   
 | |
|   =cut
 | |
|   
 | |
|   =func update_bakefile_0
 | |
|   
 | |
|   Update file with variable definitions in bakefile-0 format with the data
 | |
|   from the hash ref containing all the file lists.
 | |
|   
 | |
|   Takes the (open) file handles of the files to read and to write and the file
 | |
|   lists hash ref as arguments.
 | |
|   
 | |
|   Returns 1 if any changes were made.
 | |
|   =cut
 | |
|   
 | |
|   sub update_bakefile_0
 | |
|   {
 | |
|       my ($in, $out, $vars) = @_;
 | |
|   
 | |
|       # Variable whose contents is being currently replaced.
 | |
|       my $var;
 | |
|   
 | |
|       # Hash with files defined for the specified variable as keys and 0 or 1
 | |
|       # depending on whether we have seen them in the input file as values.
 | |
|       my %files;
 | |
|   
 | |
|       # Set to 1 if we made any changes.
 | |
|       my $changed = 0;
 | |
|       while (<$in>) {
 | |
|           chomp;
 | |
|   
 | |
|           if (/<set var="(\w+)" hints="files">/ && exists $vars->{$1}) {
 | |
|               $var = $1;
 | |
|               %files = map { $_ => 0 } @{$vars->{$var}};
 | |
|           } elsif (defined $var) {
 | |
|               local $_ = $_;
 | |
|               s/<!-- .* -->//;
 | |
|               s/^\s+//;
 | |
|               s/\s+$//;
 | |
|               s{<if [^>]+>}{};
 | |
|               s{</if>}{};
 | |
|               if (m{</set>}) {
 | |
|                   # Check if we have any new files.
 | |
|                   #
 | |
|                   # TODO Insert them in alphabetical order.
 | |
|                   while (my ($file, $seen) = each(%files)) {
 | |
|                       if (!$seen) {
 | |
|                           # This file was wasn't present in the input, add it.
 | |
|                           # TODO Use proper indentation.
 | |
|                           print $out "    $file\n";
 | |
|   
 | |
|                           $changed = 1;
 | |
|                       }
 | |
|                   }
 | |
|   
 | |
|                   undef $var;
 | |
|               } elsif ($_) {
 | |
|                   if (not exists $files{$_}) {
 | |
|                       # This file was removed.
 | |
|                       $changed = 1;
 | |
|                       next;
 | |
|                   }
 | |
|   
 | |
|                   if ($files{$_}) {
 | |
|                       warn qq{Duplicate file "$_" in the definition of the } .
 | |
|                            qq{variable "$var" at line $.\n}
 | |
|                   } else {
 | |
|                       $files{$_} = 1;
 | |
|                   }
 | |
|               }
 | |
|           }
 | |
|   
 | |
|           print $out "$_\n";
 | |
|       }
 | |
|   
 | |
|       $changed
 | |
|   }
 | |
| MAKEFILE_UPDATE_BAKEFILE0
 | |
| 
 | |
| $fatpacked{"Makefile/Update/CMakefile.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'MAKEFILE_UPDATE_CMAKEFILE';
 | |
|   package Makefile::Update::CMakefile;
 | |
|   # ABSTRACT: Update lists of files in CMake variables.
 | |
|   
 | |
|   use Exporter qw(import);
 | |
|   our @EXPORT = qw(update_cmakefile);
 | |
|   
 | |
|   use strict;
 | |
|   use warnings;
 | |
|   
 | |
|   # VERSION
 | |
|   
 | |
|   =head1 SYNOPSIS
 | |
|   
 | |
|   This can be used to update the contents of a variable containing a list of
 | |
|   files in a CMake file.
 | |
|   
 | |
|       use Makefile::Update::CMakefile;
 | |
|       Makefile::Update::upmake('CMakeLists.txt', \&update_cmakefile, $vars);
 | |
|   
 | |
|   =head1 SEE ALSO
 | |
|   
 | |
|   Makefile::Update
 | |
|   
 | |
|   =cut
 | |
|   
 | |
|   # Variables in our input files use make-like $(var) syntax while CMake uses
 | |
|   # shell-like ${var}, so convert to the target format.
 | |
|   sub _var_to_cmake
 | |
|   {
 | |
|       my ($var) = @_;
 | |
|       $var =~ s/\((\w+)\)/{$1}/g;
 | |
|       $var;
 | |
|   }
 | |
|   
 | |
|   =func update_cmakefile
 | |
|   
 | |
|   Update variable definitions in a CMake file with the data from the hash
 | |
|   ref containing all the file lists.
 | |
|   
 | |
|   The variables are supposed to be defined in the following format:
 | |
|   
 | |
|       set(var
 | |
|           foo
 | |
|           bar
 | |
|           baz
 | |
|       )
 | |
|   
 | |
|   Notably, each file has to be on its own line, including the first one.
 | |
|   
 | |
|   Takes the (open) file handles of the files to read and to write and the file
 | |
|   lists hash ref as arguments.
 | |
|   
 | |
|   Returns 1 if any changes were made.
 | |
|   =cut
 | |
|   
 | |
|   sub update_cmakefile
 | |
|   {
 | |
|       my ($in, $out, $vars) = @_;
 | |
|   
 | |
|       # Variable whose contents is being currently replaced.
 | |
|       my $var;
 | |
|   
 | |
|       # Hash with files defined for the specified variable as keys and 0 or 1
 | |
|       # depending on whether we have seen them in the input file as values.
 | |
|       my %files;
 | |
|   
 | |
|       # Set to 1 if we made any changes.
 | |
|       my $changed = 0;
 | |
|       while (<$in>) {
 | |
|           # Preserve the original line to be able to output it with any comments
 | |
|           # that we strip below.
 | |
|           my $line_orig = $_;
 | |
|   
 | |
|           # Get rid of white space and comments.
 | |
|           chomp;
 | |
|           s/^\s+//;
 | |
|           s/\s+$//;
 | |
|           s/ *#.*$//;
 | |
|   
 | |
|           # Are we inside a variable definition?
 | |
|           if (defined $var) {
 | |
|               if (/^\)$/) {
 | |
|                   # End of variable definition, check if we have any new files.
 | |
|                   #
 | |
|                   # TODO Insert them in alphabetical order.
 | |
|                   while (my ($file, $seen) = each(%files)) {
 | |
|                       if (!$seen) {
 | |
|                           # This file wasn't present in the input, add it.
 | |
|                           # TODO Use proper indentation.
 | |
|                           print $out "    $file\n";
 | |
|   
 | |
|                           $changed = 1;
 | |
|                       }
 | |
|                   }
 | |
|   
 | |
|                   undef $var;
 | |
|               } elsif ($_) {
 | |
|                   # We're inside a variable definition.
 | |
|                   if (not exists $files{$_}) {
 | |
|                       # This file was removed.
 | |
|                       $changed = 1;
 | |
|                       next;
 | |
|                   }
 | |
|   
 | |
|                   if ($files{$_}) {
 | |
|                       warn qq{Duplicate file "$_" in the definition of the } .
 | |
|                            qq{variable "$var" at line $.\n}
 | |
|                   } else {
 | |
|                       $files{$_} = 1;
 | |
|                   }
 | |
|               }
 | |
|           } elsif (/^set *\( *(\w+)$/ && exists $vars->{$1}) {
 | |
|               # Start of a new variable definition.
 | |
|               $var = $1;
 | |
|   
 | |
|               %files = map { _var_to_cmake($_) => 0 } @{$vars->{$var}};
 | |
|           }
 | |
|   
 | |
|           print $out $line_orig;
 | |
|       }
 | |
|   
 | |
|       $changed
 | |
|   }
 | |
| MAKEFILE_UPDATE_CMAKEFILE
 | |
| 
 | |
| $fatpacked{"Makefile/Update/MSBuild.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'MAKEFILE_UPDATE_MSBUILD';
 | |
|   package Makefile::Update::MSBuild;
 | |
|   # ABSTRACT: Update list of sources and headers in MSBuild projects.
 | |
|   
 | |
|   use Exporter qw(import);
 | |
|   our @EXPORT = qw(update_msbuild_project update_msbuild update_msbuild_filters);
 | |
|   
 | |
|   use strict;
 | |
|   use warnings;
 | |
|   
 | |
|   # VERSION
 | |
|   
 | |
|   =head1 SYNOPSIS
 | |
|   
 | |
|   Given an MSBuild project C<project.vcxproj> and its associated filters file
 | |
|   C<projects.vcxproj.filters>, the functions in this module can be used to update
 | |
|   the list of files in them to correspond to the given ones.
 | |
|   
 | |
|       use Makefile::Update::MSBuild;
 | |
|       upmake_msbuild_project('project.vcxproj', \@sources, \@headers);
 | |
|   
 | |
|   =head1 SEE ALSO
 | |
|   
 | |
|   Makefile::Update, Makefile::Update::VCProj
 | |
|   
 | |
|   =cut
 | |
|   
 | |
|   =func update_msbuild_project
 | |
|   
 | |
|   Update sources and headers in an MSBuild project and filter files.
 | |
|   
 | |
|   Pass the path of the project to update or a hash with the same keys as used by
 | |
|   C<Makefile::Update::upmake> as the first parameter and the references to the
 | |
|   sources and headers arrays as the subsequent ones.
 | |
|   
 | |
|   Returns 1 if any changes were made, either to the project itself or to its
 | |
|   associated C<.filters> file.
 | |
|   =cut
 | |
|   
 | |
|   sub update_msbuild_project
 | |
|   {
 | |
|       my ($file_or_options, $sources, $headers) = @_;
 | |
|   
 | |
|       use Makefile::Update;
 | |
|   
 | |
|       if (!Makefile::Update::upmake($file_or_options,
 | |
|                   \&update_msbuild, $sources, $headers
 | |
|               )) {
 | |
|           return 0;
 | |
|       }
 | |
|   
 | |
|       my $args;
 | |
|       if (ref $file_or_options eq 'HASH') {
 | |
|           # Need to make a copy to avoid modifying the callers hash.
 | |
|           $args = { %$file_or_options };
 | |
|           $args->{file} .= ".filters"
 | |
|       } else {
 | |
|           $args = "$file_or_options.filters"
 | |
|       }
 | |
|   
 | |
|       return Makefile::Update::upmake($args,
 | |
|                   \&update_msbuild_filters, $sources, $headers
 | |
|               );
 | |
|   }
 | |
|   
 | |
|   
 | |
|   =func update_msbuild
 | |
|   
 | |
|   Update sources and headers in an MSBuild project.
 | |
|   
 | |
|   Parameters: input and output file handles and array references to the sources
 | |
|   and the headers to be used in this project.
 | |
|   
 | |
|   Returns 1 if any changes were made.
 | |
|   =cut
 | |
|   
 | |
|   sub update_msbuild
 | |
|   {
 | |
|       my ($in, $out, $sources, $headers) = @_;
 | |
|   
 | |
|       # Hashes mapping the sources/headers names to 1 if they have been seen in
 | |
|       # the project or 0 otherwise.
 | |
|       my %sources = map { $_ => 0 } @$sources;
 | |
|       my %headers = map { $_ => 0 } @$headers;
 | |
|   
 | |
|       # Reference to the hash corresponding to the files currently being
 | |
|       # processed.
 | |
|       my $files;
 | |
|   
 | |
|       # Set to 1 when we are inside any <ItemGroup> tag.
 | |
|       my $in_group = 0;
 | |
|   
 | |
|       # Set to 1 when we are inside an item group containing sources or headers
 | |
|       # respectively.
 | |
|       my ($in_sources, $in_headers) = 0;
 | |
|   
 | |
|       # Set to 1 if we made any changes.
 | |
|       my $changed = 0;
 | |
|       while (my $line_with_eol = <$in>) {
 | |
|           (my $line = $line_with_eol) =~ s/\r?\n?$//;
 | |
|   
 | |
|           if ($line =~ /^\s*<ItemGroup>$/) {
 | |
|               $in_group = 1;
 | |
|           } elsif ($line =~ m{^\s*</ItemGroup>$}) {
 | |
|               if (defined $files) {
 | |
|                   my $kind = $in_sources ? 'Compile' : 'Include';
 | |
|   
 | |
|                   # Check if we have any new files.
 | |
|                   #
 | |
|                   # TODO Insert them in alphabetical order.
 | |
|                   while (my ($file, $seen) = each(%$files)) {
 | |
|                       if (!$seen) {
 | |
|                           # Convert path separator to the one used by MSBuild.
 | |
|                           $file =~ s@/@\\@g;
 | |
|   
 | |
|                           print $out qq{    <Cl$kind Include="$file" />\r\n};
 | |
|   
 | |
|                           $changed = 1;
 | |
|                       }
 | |
|                   }
 | |
|   
 | |
|                   $in_sources = $in_headers = 0;
 | |
|                   $files = undef;
 | |
|               }
 | |
|   
 | |
|               $in_group = 0;
 | |
|           } elsif ($in_group) {
 | |
|               if ($line =~ m{^\s*<Cl(?<kind>Compile|Include) Include="(?<file>[^"]+)"\s*(?<slash>/)?>$}) {
 | |
|                   my $kind = $+{kind};
 | |
|                   if ($kind eq 'Compile') {
 | |
|                       warn "Mix of sources and headers at line $.\n" if $in_headers;
 | |
|                       $in_sources = 1;
 | |
|                       $files = \%sources;
 | |
|                   } else {
 | |
|                       warn "Mix of headers and sources at line $.\n" if $in_sources;
 | |
|                       $in_headers = 1;
 | |
|                       $files = \%headers;
 | |
|                   }
 | |
|   
 | |
|                   my $closed_tag = defined $+{slash};
 | |
|   
 | |
|                   # Normalize the path separator, we always use Unix ones but the
 | |
|                   # project files use Windows one.
 | |
|                   my $file = $+{file};
 | |
|                   $file =~ s@\\@/@g;
 | |
|   
 | |
|                   if (not exists $files->{$file}) {
 | |
|                       # This file was removed.
 | |
|                       $changed = 1;
 | |
|   
 | |
|                       if (!$closed_tag) {
 | |
|                           # We have just the opening <ClCompile> or <ClInclude>
 | |
|                           # tag, ignore everything until the matching closing one.
 | |
|                           my $tag = "Cl$kind";
 | |
|                           while (<$in>) {
 | |
|                               last if m{^\s*</$tag>\r?\n$};
 | |
|                           }
 | |
|                       }
 | |
|   
 | |
|                       # In any case skip either this line containing the full
 | |
|                       # <ClCompile/> tag or the line with the closing tag.
 | |
|                       next;
 | |
|                   } else {
 | |
|                       if ($files->{$file}) {
 | |
|                           warn qq{Duplicate file "$file" in the project at line $.\n};
 | |
|                       } else {
 | |
|                           $files->{$file} = 1;
 | |
|                       }
 | |
|                   }
 | |
|               }
 | |
|           }
 | |
|   
 | |
|           print $out $line_with_eol;
 | |
|       }
 | |
|   
 | |
|       $changed
 | |
|   }
 | |
|   
 | |
|   =func update_msbuild_filters
 | |
|   
 | |
|   Update sources and headers in an MSBuild filters file.
 | |
|   
 | |
|   Parameters: input and output file handles, array references to the sources
 | |
|   and the headers to be used in this project and a callback used to determine
 | |
|   the filter for the new files.
 | |
|   
 | |
|   Returns 1 if any changes were made.
 | |
|   =cut
 | |
|   
 | |
|   sub update_msbuild_filters
 | |
|   {
 | |
|       my ($in, $out, $sources, $headers, $filter_cb) = @_;
 | |
|   
 | |
|       # Use standard/default classifier for the files if none is explicitly
 | |
|       # specified.
 | |
|       if (!defined $filter_cb) {
 | |
|           $filter_cb = sub {
 | |
|               my ($file) = @_;
 | |
|   
 | |
|               return 'Source Files' if $file =~ q{\.c(c|pp|xx|\+\+)?$};
 | |
|               return 'Header Files' if $file =~ q{\.h(h|pp|xx|\+\+)?$};
 | |
|   
 | |
|               warn qq{No filter defined for the file "$file".\n};
 | |
|   
 | |
|               undef
 | |
|           }
 | |
|       }
 | |
|   
 | |
|       # Hashes mapping the sources/headers names to the text representing them in
 | |
|       # the input file if they have been seen in it or nothing otherwise.
 | |
|       my %sources = map { $_ => undef } @$sources;
 | |
|       my %headers = map { $_ => undef } @$headers;
 | |
|   
 | |
|       # Reference to the hash corresponding to the files currently being
 | |
|       # processed.
 | |
|       my $files;
 | |
|   
 | |
|       # Set to 1 when we are inside any <ItemGroup> tag.
 | |
|       my $in_group = 0;
 | |
|   
 | |
|       # Set to 1 when we are inside an item group containing sources or headers
 | |
|       # respectively.
 | |
|       my ($in_sources, $in_headers) = 0;
 | |
|   
 | |
|       # Set to 1 if we made any changes.
 | |
|       my $changed = 0;
 | |
|       while (my $line_with_eol = <$in>) {
 | |
|           (my $line = $line_with_eol) =~ s/\r?\n?$//;
 | |
|   
 | |
|           if ($line =~ /^\s*<ItemGroup>?$/) {
 | |
|               $in_group = 1;
 | |
|           } elsif ($line =~ m{^\s*</ItemGroup>?$}) {
 | |
|               if (defined $files) {
 | |
|                   # Output the group contents now, all at once, inserting any new
 | |
|                   # files: we must do it like this to ensure that they are
 | |
|                   # inserted in alphabetical order.
 | |
|                   my $kind = $in_sources ? 'Compile' : 'Include';
 | |
|   
 | |
|                   foreach my $file (sort keys %$files) {
 | |
|                       if (defined $files->{$file}) {
 | |
|                           print $out $files->{$file};
 | |
|                       } else {
 | |
|                           my $filter = $filter_cb->($file);
 | |
|   
 | |
|                           # Convert path separator to the one used by MSBuild.
 | |
|                           $file =~ s@/@\\@g;
 | |
|   
 | |
|                           my $indent = ' ' x 2;
 | |
|   
 | |
|                           print $out qq{$indent$indent<Cl$kind Include="$file"};
 | |
|                           if (defined $filter) {
 | |
|                               print $out ">\r\n$indent$indent$indent<Filter>$filter</Filter>\r\n$indent$indent</Cl$kind>\r\n";
 | |
|                           } else {
 | |
|                               print $out " />\r\n";
 | |
|                           }
 | |
|   
 | |
|                           $changed = 1;
 | |
|                       }
 | |
|                   }
 | |
|   
 | |
|                   $in_sources = $in_headers = 0;
 | |
|                   $files = undef;
 | |
|               }
 | |
|   
 | |
|               $in_group = 0;
 | |
|           } elsif ($in_group &&
 | |
|                    $line =~ m{^\s*<Cl(?<kind>Compile|Include) Include="(?<file>[^"]+)"\s*(?<slash>/)?>?$}) {
 | |
|               my $kind = $+{kind};
 | |
|               if ($kind eq 'Compile') {
 | |
|                   warn "Mix of sources and headers at line $.\n" if $in_headers;
 | |
|                   $in_sources = 1;
 | |
|                   $files = \%sources;
 | |
|               } else {
 | |
|                   warn "Mix of headers and sources at line $.\n" if $in_sources;
 | |
|                   $in_headers = 1;
 | |
|                   $files = \%headers;
 | |
|               }
 | |
|   
 | |
|               my $closed_tag = defined $+{slash};
 | |
|   
 | |
|               # Normalize the path separator, we always use Unix ones but the
 | |
|               # project files use Windows one.
 | |
|               my $file = $+{file};
 | |
|               $file =~ s@\\@/@g;
 | |
|   
 | |
|               my $text = $line_with_eol;
 | |
|               if (!$closed_tag) {
 | |
|                   # We have just the opening <ClCompile> tag, get everything
 | |
|                   # until the next </ClCompile>.
 | |
|                   while (<$in>) {
 | |
|                       $text .= $_;
 | |
|                       last if m{^\s*</Cl$kind>\r?\n?$};
 | |
|                   }
 | |
|               }
 | |
|   
 | |
|               if (not exists $files->{$file}) {
 | |
|                   # This file was removed.
 | |
|                   $changed = 1;
 | |
|               } else {
 | |
|                   if ($files->{$file}) {
 | |
|                       warn qq{Duplicate file "$file" in the project at line $.\n};
 | |
|                   } else {
 | |
|                       $files->{$file} = $text;
 | |
|                   }
 | |
|               }
 | |
|   
 | |
|               # Don't output this line yet, wait until the end of the group.
 | |
|               next
 | |
|           }
 | |
|   
 | |
|           print $out $line_with_eol;
 | |
|       }
 | |
|   
 | |
|       $changed
 | |
|   }
 | |
| MAKEFILE_UPDATE_MSBUILD
 | |
| 
 | |
| $fatpacked{"Makefile/Update/Makefile.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'MAKEFILE_UPDATE_MAKEFILE';
 | |
|   package Makefile::Update::Makefile;
 | |
|   # ABSTRACT: Update lists of files in makefile variables.
 | |
|   
 | |
|   use Exporter qw(import);
 | |
|   our @EXPORT = qw(update_makefile);
 | |
|   
 | |
|   use strict;
 | |
|   use warnings;
 | |
|   
 | |
|   # VERSION
 | |
|   
 | |
|   =head1 SYNOPSIS
 | |
|   
 | |
|   This can be used to update the contents of a variable containing a list of
 | |
|   files in a makefile.
 | |
|   
 | |
|       use Makefile::Update::Makefile;
 | |
|       Makefile::Update::upmake('GNUmakefile', \&update_makefile, $vars);
 | |
|   
 | |
|   =head1 SEE ALSO
 | |
|   
 | |
|   Makefile::Update
 | |
|   
 | |
|   =cut
 | |
|   
 | |
|   =func update_makefile
 | |
|   
 | |
|   Update variable definitions in a makefile format with the data from the hash
 | |
|   ref containing all the file lists.
 | |
|   
 | |
|   Only most straightforward cases of variable or target definitions are
 | |
|   recognized here, i.e. just "var := value", "var = value" or "target: value".
 | |
|   In particular we don't support any GNU make extensions such as "export" or
 | |
|   "override" without speaking of anything more complex.
 | |
|   
 | |
|   On top of it, currently the value should contain a single file per line with
 | |
|   none at all on the first line (but this restriction could be relaxed later if
 | |
|   needed), i.e. the only supported case is
 | |
|   
 | |
|       var = \
 | |
|             foo \
 | |
|             bar \
 | |
|             baz
 | |
|   
 | |
|   and it must be followed by an empty line, too.
 | |
|   
 | |
|   Notice that if any of the "files" in the variable value looks like a makefile
 | |
|   variable, i.e. has "$(foo)" form, it is ignored by this function, i.e. not
 | |
|   removed even if it doesn't appear in the list of files (which will never be
 | |
|   the case normally).
 | |
|   
 | |
|   Takes the (open) file handles of the files to read and to write and the file
 | |
|   lists hash ref as arguments.
 | |
|   
 | |
|   Returns 1 if any changes were made.
 | |
|   =cut
 | |
|   
 | |
|   sub update_makefile
 | |
|   {
 | |
|       my ($in, $out, $vars) = @_;
 | |
|   
 | |
|       # Variable whose contents is being currently replaced and its original
 | |
|       # name in the makefile.
 | |
|       my ($var, $makevar);
 | |
|   
 | |
|       # Hash with files defined for the specified variable as keys and 0 or 1
 | |
|       # depending on whether we have seen them in the input file as values.
 | |
|       my %files;
 | |
|   
 | |
|       # Array of lines in the existing makefile.
 | |
|       my @values;
 | |
|   
 | |
|       # True if the values are in alphabetical order: we use this to add new
 | |
|       # entries in alphabetical order too if the existing ones use it, otherwise
 | |
|       # we just append them at the end.
 | |
|       my $sorted = 1;
 | |
|   
 | |
|       # Extensions of the files in the files list (they're keys of this hash,
 | |
|       # the values are not used), there can be more than one (e.g. ".c" and
 | |
|       # ".cpp").
 | |
|       my %src_exts;
 | |
|   
 | |
|       # Extension of the files in the makefiles: here there can also be more
 | |
|       # than one, but in this case we just give up and don't perform any
 | |
|       # extensions translation because we don't have enough information to do it
 | |
|       # (e.g. which extension should be used for the new files in the makefile?).
 | |
|       # Such case is indicated by make_ext being empty (as opposed to its
 | |
|       # initial undefined value).
 | |
|       my $make_ext;
 | |
|   
 | |
|       # Helper to get the extension. Note that the "extension" may be a make
 | |
|       # variable, e.g. the file could be something like "foo.$(obj)", so don't
 | |
|       # restrict it to just word characters.
 | |
|       sub _get_ext { $_[0] =~ /(\.\S+)$/ ? $1 : undef }
 | |
|   
 | |
|       # Indent and the part after the value (typically some amount of spaces and
 | |
|       # a backslash) for normal lines and, separately, for the last one, as it
 | |
|       # may or not have backslash after it.
 | |
|       my ($indent, $tail, $last_tail);
 | |
|   
 | |
|       # We can't use the usual check for EOF inside while itself because this
 | |
|       # wouldn't work for files with no new line after the last line, so check
 | |
|       # for the EOF manually.
 | |
|       my $eof = 0;
 | |
|   
 | |
|       # Set to 1 if we made any changes.
 | |
|       my $changed = 0;
 | |
|       while (1) {
 | |
|           my $line = <$in>;
 | |
|           if (defined $line) {
 | |
|               chomp $line;
 | |
|           } else {
 | |
|               $line = '';
 | |
|               $eof = 1;
 | |
|           }
 | |
|   
 | |
|           # If we're inside the variable definition, parse the current line as
 | |
|           # another file name,
 | |
|           if (defined $var) {
 | |
|               if ($line =~ /^(?<indent>\s*)(?<file>[^ ]+)(?<tail>\s*\\?)$/) {
 | |
|                   if (defined $indent) {
 | |
|                       warn qq{Inconsistent indent at line $. in the } .
 | |
|                            qq{definition of the variable "$makevar".\n}
 | |
|                           if $+{indent} ne $indent;
 | |
|                   } else {
 | |
|                       $indent = $+{indent};
 | |
|                   }
 | |
|   
 | |
|                   $last_tail = $+{tail};
 | |
|                   my $file_orig = $+{file};
 | |
|   
 | |
|                   $tail = $last_tail if !defined $tail;
 | |
|   
 | |
|                   # Check if we have something with the correct extension and
 | |
|                   # preserve unchanged all the rest -- we don't want to remove
 | |
|                   # expansions of other makefile variables from this one, for
 | |
|                   # example, but such expansions would never be in the files
 | |
|                   # list as they don't make sense for the other formats.
 | |
|                   my $file = $file_orig;
 | |
|                   if (defined (my $file_ext = _get_ext($file))) {
 | |
|                       if (defined $make_ext) {
 | |
|                           if ($file_ext ne $make_ext) {
 | |
|                               # As explained in the comment before make_ext
 | |
|                               # definition, just don't do anything in this case.
 | |
|                               $make_ext = '';
 | |
|                           }
 | |
|                       } else {
 | |
|                           $make_ext = $file_ext;
 | |
|                       }
 | |
|   
 | |
|                       # We need to try this file with all of the source
 | |
|                       # extensions we have as it can correspond to any of them.
 | |
|                       for my $src_ext (keys %src_exts) {
 | |
|                           if ($file_ext ne $src_ext) {
 | |
|                               (my $file_try = $file) =~ s/\Q$file_ext\E$/$src_ext/;
 | |
|                               if (exists $files{$file_try}) {
 | |
|                                   $file = $file_try;
 | |
|                                   last
 | |
|                               }
 | |
|                           }
 | |
|                       }
 | |
|   
 | |
|                       if (!exists $files{$file}) {
 | |
|                           # This file was removed.
 | |
|                           $changed = 1;
 | |
|   
 | |
|                           # Don't store this line in @values below.
 | |
|                           next;
 | |
|                       }
 | |
|                   }
 | |
|   
 | |
|                   if (exists $files{$file}) {
 | |
|                       if ($files{$file}) {
 | |
|                           warn qq{Duplicate file "$file" in the definition of the } .
 | |
|                                qq{variable "$makevar" at line $.\n}
 | |
|                       } else {
 | |
|                           $files{$file} = 1;
 | |
|                       }
 | |
|                   }
 | |
|   
 | |
|                   # Are we still sorted?
 | |
|                   if (@values && lc $line lt $values[-1]) {
 | |
|                       $sorted = 0;
 | |
|                   }
 | |
|   
 | |
|                   push @values, $line;
 | |
|                   next;
 | |
|               }
 | |
|   
 | |
|               # If the last line had a continuation character, the file list
 | |
|               # should only end if there is nothing else on the following line.
 | |
|               if ($last_tail =~ /\\$/ && $line =~ /\S/) {
 | |
|                   warn qq{Expected blank line at line $..\n};
 | |
|               }
 | |
|   
 | |
|               # End of variable definition, add new lines.
 | |
|   
 | |
|               # We can only map the extensions if we have a single extension to
 | |
|               # map them to (i.e. make_ext is not empty) and we only need to do
 | |
|               # it if are using more than one extension in the source files list
 | |
|               # or the single extension that we use is different from make_ext.
 | |
|               if (defined $make_ext) {
 | |
|                   if ($make_ext eq '' ||
 | |
|                           (keys %src_exts == 1 && exists $src_exts{$make_ext})) {
 | |
|                       undef $make_ext
 | |
|                   }
 | |
|               }
 | |
|   
 | |
|               my $new_files = 0;
 | |
|               while (my ($file, $seen) = each(%files)) {
 | |
|                   next if $seen;
 | |
|   
 | |
|                   # This file was wasn't present in the input, add it.
 | |
|   
 | |
|                   # If this is the first file we add, ensure that the last line
 | |
|                   # present in the makefile so far has the line continuation
 | |
|                   # character at the end as this might not have been the case.
 | |
|                   if (!$new_files) {
 | |
|                       $new_files = 1;
 | |
|   
 | |
|                       if (@values && $values[-1] !~ /\\$/) {
 | |
|                           $values[-1] .= $tail;
 | |
|                       }
 | |
|                   }
 | |
|   
 | |
|                   # Next give it the right extension.
 | |
|                   if (defined $make_ext) {
 | |
|                       $file =~ s/\.\S+$/$make_ext/
 | |
|                   }
 | |
|   
 | |
|                   # Finally store it.
 | |
|                   push @values, "$indent$file$tail";
 | |
|               }
 | |
|   
 | |
|               if ($new_files) {
 | |
|                   $changed = 1;
 | |
|   
 | |
|                   # Sort them if necessary using the usual Schwartzian transform.
 | |
|                   if ($sorted) {
 | |
|                       @values = map { $_->[0] }
 | |
|                                 sort { $a->[1] cmp $b->[1] }
 | |
|                                 map { [$_, lc $_] } @values;
 | |
|                   }
 | |
|   
 | |
|                   # Fix up the tail of the last line to be the same as that of
 | |
|                   # the previous last line.
 | |
|                   $values[-1] =~ s/\s*\\$/$last_tail/;
 | |
|               }
 | |
|   
 | |
|               undef $var;
 | |
|   
 | |
|               print $out join("\n", @values), "\n";
 | |
|           }
 | |
|   
 | |
|           # We're only interested in variable or target declarations, and does
 | |
|           # not look like target-specific variable (this would contain an equal
 | |
|           # sign after the target).
 | |
|           if ($line =~ /^\s*(?<var>\S+)\s*(?::?=|:)(?<tail>[^=]*)$/) {
 | |
|               $makevar = $+{var};
 | |
|               my $tail = $+{tail};
 | |
|   
 | |
|               # And only those of them for which we have values, but this is
 | |
|               # where it gets tricky as we try to be smart to accommodate common
 | |
|               # use patterns with minimal effort.
 | |
|               if (exists $vars->{$makevar}) {
 | |
|                   $var = $makevar;
 | |
|               } else {
 | |
|                   # Helper: return name if a variable with such name exists or
 | |
|                   # undef otherwise.
 | |
|                   my $var_if_exists = sub { exists $vars->{$_[0]} ? $_[0] : undef };
 | |
|   
 | |
|                   if ($makevar =~ /^objects$/i || $makevar =~ /^obj$/i) {
 | |
|                       # Special case: map it to "sources" as we work with the
 | |
|                       # source, not object, files.
 | |
|                       $var = $var_if_exists->('sources');
 | |
|                   } elsif ($makevar =~ /^(\w+)_(objects|obj|sources|src)$/i) {
 | |
|                       # Here we deal with "foo_sources" typically found in
 | |
|                       # hand-written makefiles but also "foo_SOURCES" used in
 | |
|                       # automake ones, but the latter also uses libfoo_a_SOURCES
 | |
|                       # for static libraries and libfoo_la_SOURCES for the
 | |
|                       # libtool libraries, be smart about it to allow defining
 | |
|                       # just "foo" or "foo_sources" variables usable with all
 | |
|                       # kinds of make/project files.
 | |
|                       $var = $var_if_exists->($1) || $var_if_exists->("$1_sources");
 | |
|                       if (!defined $var && $2 eq 'SOURCES' && $1 =~ /^(\w+)_l?a$/) {
 | |
|                           $var = $var_if_exists->($1) || $var_if_exists->("$1_sources");
 | |
|                           if (!defined $var && $1 =~ /^lib(\w+)$/) {
 | |
|                               $var = $var_if_exists->($1) || $var_if_exists->("$1_sources");
 | |
|                           }
 | |
|                       }
 | |
|                   } elsif ($makevar =~ /^(\w+)\$\(\w+\)/) {
 | |
|                       # This one is meant to catch relatively common makefile
 | |
|                       # constructions like "target$(exe_ext)".
 | |
|                       $var = $var_if_exists->($1);
 | |
|                   }
 | |
|               }
 | |
|   
 | |
|               if (defined $var) {
 | |
|                   if ($tail !~ /\s*\\$/) {
 | |
|                       warn qq{Unsupported format for variable "$makevar" at line $..\n};
 | |
|                       undef $var;
 | |
|                   } else {
 | |
|                       %files = map { $_ => 0 } @{$vars->{$var}};
 | |
|   
 | |
|                       @values = ();
 | |
|   
 | |
|                       # Find all the extensions used by files in this variable.
 | |
|                       for my $file (@{$vars->{$var}}) {
 | |
|                           if (defined (my $src_ext = _get_ext($file))) {
 | |
|                               $src_exts{$src_ext} = 1;
 | |
|                           }
 | |
|                       }
 | |
|   
 | |
|                       # Not known yet.
 | |
|                       undef $make_ext;
 | |
|   
 | |
|                       undef $indent;
 | |
|                       $tail = $tail;
 | |
|                       undef $last_tail;
 | |
|   
 | |
|                       # Not unsorted so far.
 | |
|                       $sorted = 1;
 | |
|                   }
 | |
|               }
 | |
|           }
 | |
|   
 | |
|           print $out "$line";
 | |
|   
 | |
|           # Don't add an extra new line at the EOF if it hadn't been there.
 | |
|           last if $eof;
 | |
|   
 | |
|           print $out "\n";
 | |
|       }
 | |
|   
 | |
|       $changed
 | |
|   }
 | |
| MAKEFILE_UPDATE_MAKEFILE
 | |
| 
 | |
| $fatpacked{"Makefile/Update/VCProj.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'MAKEFILE_UPDATE_VCPROJ';
 | |
|   package Makefile::Update::VCProj;
 | |
|   # ABSTRACT: Update list of sources and headers in Visual C++ projects.
 | |
|   
 | |
|   use Exporter qw(import);
 | |
|   our @EXPORT = qw(update_vcproj);
 | |
|   
 | |
|   use strict;
 | |
|   use warnings;
 | |
|   
 | |
|   # VERSION
 | |
|   
 | |
|   =head1 SYNOPSIS
 | |
|   
 | |
|   The function L<update_vcproj()> can be used to update the list of headers and
 | |
|   sources in the given Visual C++ project file C<project.vcproj>:
 | |
|   
 | |
|       use Makefile::Update::VCProj;
 | |
|       upmake_msbuild_project('project.vcproj', \@sources, \@headers);
 | |
|   
 | |
|   =head1 SEE ALSO
 | |
|   
 | |
|   Makefile::Update, Makefile::Update::MSBuild
 | |
|   
 | |
|   =cut
 | |
|   
 | |
|   =func update_vcproj
 | |
|   
 | |
|   Update sources and headers in a VC++ project.
 | |
|   
 | |
|   Parameters: input and output file handles, array references to the sources
 | |
|   and the headers to be used in this project and a callback used to determine
 | |
|   the filter for the new files.
 | |
|   
 | |
|   Returns 1 if any changes were made.
 | |
|   =cut
 | |
|   
 | |
|   sub update_vcproj
 | |
|   {
 | |
|       my ($in, $out, $sources, $headers, $filter_cb) = @_;
 | |
|   
 | |
|       # Use standard/default classifier for the files if none is explicitly
 | |
|       # specified.
 | |
|       if (!defined $filter_cb) {
 | |
|           $filter_cb = sub {
 | |
|               my ($file) = @_;
 | |
|   
 | |
|               return 'Source Files' if $file =~ q{\.c(c|pp|xx|\+\+)?$};
 | |
|               return 'Header Files' if $file =~ q{\.h(h|pp|xx|\+\+)?$};
 | |
|   
 | |
|               warn qq{No filter defined for the file "$file".\n};
 | |
|   
 | |
|               undef
 | |
|           }
 | |
|       }
 | |
|   
 | |
|       # Hash mapping the filter to all the files using it (whether sources or
 | |
|       # headers).
 | |
|       my %files_by_filter;
 | |
|       foreach my $file (@$sources, @$headers) {
 | |
|           my $filter = $filter_cb->($file);
 | |
|           if (defined $filter) {
 | |
|               push @{$files_by_filter{$filter}}, $file
 | |
|           }
 | |
|       }
 | |
|   
 | |
|       # Name of the current filter, if any.
 | |
|       my $filter;
 | |
|   
 | |
|       # Hash containing 0 or 1 for each file using the current filter.
 | |
|       my %seen;
 | |
|   
 | |
|       # Indicates whether the closing angle bracket of "<File>" tags is on its
 | |
|       # own line (which is how MSVS 2005 and 2008 format their files) or on the
 | |
|       # same line as "RelativePath" attribute (which is how MSVS 2003 does it).
 | |
|       my $angle_bracket_on_same_line = 0;
 | |
|   
 | |
|       # Set to 1 if we made any changes.
 | |
|       my $changed = 0;
 | |
|   
 | |
|       while (defined (my $line_with_eol = <$in>)) {
 | |
|           (my $line = $line_with_eol) =~ s/\r?\n$//;
 | |
|   
 | |
|           if ($line =~ /^\s*<Filter$/) {
 | |
|               if (defined($filter)) {
 | |
|                   warn qq{Nested <Filter> tag at line $. while parsing filter } .
 | |
|                        qq{"$filter" is not supported.\n};
 | |
|                   next;
 | |
|               }
 | |
|   
 | |
|               print $out $line_with_eol;
 | |
|               $line_with_eol = <$in>;
 | |
|               if (defined $line_with_eol &&
 | |
|                       $line_with_eol =~ /^\s*Name="(.*)"\r?\n$/) {
 | |
|                   $filter = $1;
 | |
|                   if (!exists $files_by_filter{$filter}) {
 | |
|                       # If we don't have any files for this filter, don't remove
 | |
|                       # all the files from it, just skip it entirely instead.
 | |
|                       undef $filter;
 | |
|                   } else {
 | |
|                       %seen = map { $_ => 0 } @{$files_by_filter{$filter}};
 | |
|                   }
 | |
|               } else {
 | |
|                   warn qq{Unrecognized format for <Filter> tag at line $..\n};
 | |
|               }
 | |
|           } elsif (defined $filter) {
 | |
|               if ($line =~ /^\s*<File$/) {
 | |
|                   my $line_file_start = $line_with_eol;
 | |
|   
 | |
|                   $line_with_eol = <$in>;
 | |
|                   if (defined $line_with_eol &&
 | |
|                           $line_with_eol =~ /^\s*RelativePath="(.*)"(>?)\r?\n$/) {
 | |
|                       $angle_bracket_on_same_line = $2 eq '>';
 | |
|   
 | |
|                       # Normalize path separators to Unix and remove the leading
 | |
|                       # dot which MSVC likes to use for some reason.
 | |
|                       (my $file = $1) =~ s@\\@/@g;
 | |
|                       $file =~ s@^\./@@;
 | |
|   
 | |
|                       # Special hack for resource files that sometimes occur in
 | |
|                       # the "Source Files" section of MSVC projects too: don't
 | |
|                       # remove them, even if they don't appear in the master
 | |
|                       # files list, because they are never going to appear in it.
 | |
|                       if ($file !~ /\.rc$/) {
 | |
|                           if (!exists $seen{$file}) {
 | |
|                               # This file is not in the master file list any
 | |
|                               # more, delete it from the project file as well by
 | |
|                               # not copying the lines corresponding to it to the
 | |
|                               # output.
 | |
|                               $changed = 1;
 | |
|   
 | |
|                               # Skip the next line unless we had already seen
 | |
|                               # the angle bracket.
 | |
|                               if (!$angle_bracket_on_same_line) {
 | |
|                                   if (<$in> !~ /^\s*>\r?\n$/) {
 | |
|                                       warn qq{Expected closing '>' on the line $.\n}
 | |
|                                   }
 | |
|                               }
 | |
|   
 | |
|                               # And skip everything up to and including the
 | |
|                               # closing </File> tag in any case.
 | |
|                               while (<$in>) {
 | |
|                                   last if qr{^\s*</File>\r?\n$}
 | |
|                               }
 | |
|   
 | |
|                               next;
 | |
|                           }
 | |
|   
 | |
|                           # This file is still in the files list, mark it as seen.
 | |
|                           if ($seen{$file}) {
 | |
|                               warn qq{Duplicate file "$file" in the project at line $.\n};
 | |
|                           } else {
 | |
|                               $seen{$file} = 1;
 | |
|                           }
 | |
|                       }
 | |
|                   } else {
 | |
|                       warn qq{Unrecognized format for <File> tag inside filter } .
 | |
|                            qq{"$filter" at line $..\n};
 | |
|                   }
 | |
|   
 | |
|                   # Don't lose the original line, it won't be printed at the
 | |
|                   # end of the loop any more.
 | |
|                   print $out $line_file_start;
 | |
|               } elsif ($line =~ qr{^\s*</Filter>$}) {
 | |
|                   my $angle_bracket = $angle_bracket_on_same_line
 | |
|                                           ? '>'
 | |
|                                           : "\n\t\t\t\t>";
 | |
|   
 | |
|                   # Add new files, if any.
 | |
|                   #
 | |
|                   # TODO Insert them in alphabetical order.
 | |
|                   while (my ($file, $seen) = each(%seen)) {
 | |
|                       if (!$seen) {
 | |
|                           # Convert path separator to the one used by MSVC.
 | |
|                           $file =~ s@/@\\@g;
 | |
|   
 | |
|                           # And use path even for the files in this directory.
 | |
|                           $file = ".\\$file" if $file !~ /\\/;
 | |
|   
 | |
|                           print $out <<END
 | |
|   \t\t\t<File
 | |
|   \t\t\t\tRelativePath="$file"$angle_bracket
 | |
|   \t\t\t</File>
 | |
|   END
 | |
|   ;
 | |
|   
 | |
|                           $changed = 1;
 | |
|                       }
 | |
|                   }
 | |
|   
 | |
|                   undef $filter;
 | |
|               }
 | |
|           }
 | |
|   
 | |
|           print $out $line_with_eol;
 | |
|       }
 | |
|   
 | |
|       $changed
 | |
|   }
 | |
| MAKEFILE_UPDATE_VCPROJ
 | |
| 
 | |
| s/^  //mg for values %fatpacked;
 | |
| 
 | |
| my $class = 'FatPacked::'.(0+\%fatpacked);
 | |
| no strict 'refs';
 | |
| *{"${class}::files"} = sub { keys %{$_[0]} };
 | |
| 
 | |
| if ($] < 5.008) {
 | |
|   *{"${class}::INC"} = sub {
 | |
|     if (my $fat = $_[0]{$_[1]}) {
 | |
|       my $pos = 0;
 | |
|       my $last = length $fat;
 | |
|       return (sub {
 | |
|         return 0 if $pos == $last;
 | |
|         my $next = (1 + index $fat, "\n", $pos) || $last;
 | |
|         $_ .= substr $fat, $pos, $next - $pos;
 | |
|         $pos = $next;
 | |
|         return 1;
 | |
|       });
 | |
|     }
 | |
|   };
 | |
| }
 | |
| 
 | |
| else {
 | |
|   *{"${class}::INC"} = sub {
 | |
|     if (my $fat = $_[0]{$_[1]}) {
 | |
|       open my $fh, '<', \$fat
 | |
|         or die "FatPacker error loading $_[1] (could be a perl installation issue?)";
 | |
|       return $fh;
 | |
|     }
 | |
|     return;
 | |
|   };
 | |
| }
 | |
| 
 | |
| unshift @INC, bless \%fatpacked, $class;
 | |
|   } # END OF FATPACK CODE
 | |
| 
 | |
| 
 | |
| use strict;
 | |
| use warnings;
 | |
| use autodie;
 | |
| 
 | |
| use Getopt::Long;
 | |
| 
 | |
| use FindBin qw($Bin);
 | |
| 
 | |
| use Makefile::Update;
 | |
| use Makefile::Update::Bakefile0;
 | |
| use Makefile::Update::CMakefile;
 | |
| use Makefile::Update::MSBuild;
 | |
| use Makefile::Update::VCProj;
 | |
| 
 | |
| my $verbose = 0;
 | |
| my $quiet = 0;
 | |
| my ($only_bkl, $only_msvs, $only_project, $only_version);
 | |
| 
 | |
| GetOptions(
 | |
|         'verbose|v'      => \$verbose,
 | |
|         'quiet|q'        => \$quiet,
 | |
|         'only-bkl'       => \$only_bkl,
 | |
|         'only-project=s' => sub { $only_msvs = 1; $only_project = $_[1] },
 | |
|         'only-version=i' => sub { $only_msvs = 1; $only_version = $_[1] },
 | |
|     ) or die <<EOF
 | |
| Usage: $0 [--verbose] [--quiet] [--only-bkl] [--only-project=<name>] [--only-version=<ver>]
 | |
| 
 | |
| Update the files used by bakefile and MSVC projects from the master list
 | |
| of files in build/files.
 | |
| 
 | |
| If --no-xxx option is specified, the corresponding outputs are not updated.
 | |
| By default everything is.
 | |
| 
 | |
| The version argument of --only-version can be 7, 8, 9 or 10 with the latter
 | |
| selecting the MSBuild projects.
 | |
| EOF
 | |
| ;
 | |
| 
 | |
| if ($only_bkl && $only_msvs) {
 | |
|     die qq{Options --only-bkl and --only-project or --only-version can't be used together.\n}
 | |
| }
 | |
| 
 | |
| sub call_upmake
 | |
| {
 | |
|     my ($fname, @args) = @_;
 | |
| 
 | |
|     upmake({file => $fname, quiet => $quiet, verbose => $verbose}, @args)
 | |
| }
 | |
| 
 | |
| open my $files, '<', "$Bin/files";
 | |
| my $vars = read_files_list($files);
 | |
| 
 | |
| if (!$only_msvs) {
 | |
|     if (call_upmake("$Bin/bakefiles/files.bkl", \&update_bakefile_0, $vars)) {
 | |
|         print qq{Don't forget to run "bakefile_gen -b wx.bkl".\n};
 | |
|     }
 | |
| }
 | |
| 
 | |
| if (!$only_msvs && !$only_bkl) {
 | |
|     call_upmake("$Bin/cmake/files.cmake", \&update_cmakefile, $vars);
 | |
| }
 | |
| 
 | |
| if (!$only_bkl) {
 | |
|     # Path to the project root directory from the directory containing the
 | |
|     # projects.
 | |
|     my $top_srcdir = '../../';
 | |
| 
 | |
|     # The base names of all our projects with the list of variables
 | |
|     # containing the files that should appear in them.
 | |
|     my %projects_vars = (
 | |
|             adv      => [qw(ADVANCED_CMN ADVANCED_MSW ADVANCED_MSW_DESKTOP ADVANCED_MSW_NATIVE)],
 | |
|             aui      => [qw(AUI_CMN AUI_MSW)],
 | |
|             base     => [qw(BASE_CMN BASE_AND_GUI_CMN BASE_WIN32 BASE_AND_GUI_WIN32)],
 | |
|             core     => [qw(BASE_AND_GUI_CMN BASE_AND_GUI_WIN32 MSW_LOWLEVEL MSW_DESKTOP_LOWLEVEL MSW MSW_DESKTOP GUI_CMN)],
 | |
|             gl       => [qw(OPENGL_CMN OPENGL_MSW)],
 | |
|             html     => [qw(HTML_CMN HTML_MSW)],
 | |
|             media    => [qw(MEDIA_CMN MEDIA_MSW MEDIA_MSW_DESKTOP)],
 | |
|             net      => [qw(NET_CMN NET_WIN32)],
 | |
|             propgrid => [qw(PROPGRID)],
 | |
|             qa       => [qw(QA)],
 | |
|             ribbon   => [qw(RIBBON)],
 | |
|             richtext => [qw(RICHTEXT)],
 | |
|             stc      => [qw(STC)],
 | |
|             webview  => [qw(WEBVIEW_CMN WEBVIEW_MSW)],
 | |
|             xml      => [qw(XML)],
 | |
|             xrc      => [qw(XRC)],
 | |
|         );
 | |
| 
 | |
|     # The versions of non-MSBuild projects (MSBuild ones all use version "10").
 | |
|     my @vcproj_versions = qw(7 8 9);
 | |
| 
 | |
|     # Return the "filter" to use for the given file.
 | |
|     sub filter_cb
 | |
|     {
 | |
|         my ($file) = @_;
 | |
| 
 | |
|         my %filters = (
 | |
|                 'src/common/.*'         => 'Common Sources',
 | |
|                 'src/gtk/.*'            => 'GTK+ Sources',
 | |
|                 'src/msw/.*'            => 'MSW Sources',
 | |
|                 'src/generic/.*'        => 'Generic Sources',
 | |
|                 'src/univ/.*'           => 'wxUniv Sources',
 | |
|                 'src/html/.*'           => 'wxHTML Sources',
 | |
|                 'include/.*/setup.h'    => 'Setup Headers',
 | |
|                 'include/wx/gtk/.*'     => 'GTK+ Headers',
 | |
|                 'include/wx/msw/.*'     => 'MSW Headers',
 | |
|                 'include/wx/generic/.*' => 'Generic Headers',
 | |
|                 'include/wx/univ/.*'    => 'wxUniv Headers',
 | |
|                 'include/wx/html/.*'    => 'wxHTML Headers',
 | |
|             );
 | |
| 
 | |
|         foreach (keys %filters) {
 | |
|             return $filters{$_} if $file =~ qr{^${top_srcdir}$_$};
 | |
|         }
 | |
| 
 | |
|         # Two fall backs which can't be used in the hash as they must be
 | |
|         # checked after the other patterns.
 | |
|         return 'Source Files' if $file =~ q{src/.*};
 | |
|         return 'Common Headers' if $file =~ q{include/wx/.*};
 | |
| 
 | |
|         warn qq{No filter defined for the file "$file".\n};
 | |
| 
 | |
|         undef
 | |
|     }
 | |
| 
 | |
|     foreach my $proj (sort keys %projects_vars) {
 | |
|         next if defined $only_project && $proj ne $only_project;
 | |
| 
 | |
|         my (@sources, @headers);
 | |
| 
 | |
|         # All our projects use the special dummy file for PCH creation, but it's
 | |
|         # not included in the file lists.
 | |
|         push @sources, "${top_srcdir}src/common/dummy.cpp";
 | |
| 
 | |
|         foreach my $var (@{$projects_vars{$proj}}) {
 | |
|             # The paths in the files lists are relative to the project root,
 | |
|             # so add relative path to it from the projects directory.
 | |
|             push @sources, "${top_srcdir}$_" for @{$vars->{"${var}_SRC"}};
 | |
| 
 | |
|             # It is possible that we don't have any headers of some kind at all.
 | |
|             if (exists $vars->{"${var}_HDR"}) {
 | |
|                 # Our files lists don't use the full path for the headers, the
 | |
|                 # common "include/" prefix is omitted, add it back here.
 | |
|                 push @headers, "${top_srcdir}include/$_" for @{$vars->{"${var}_HDR"}};
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         my @args = (\@sources, \@headers, \&filter_cb);
 | |
| 
 | |
|         # First deal with MSBuild project, it's the simplest case.
 | |
|         if (!defined $only_version || $only_version == 10) {
 | |
|             call_upmake("$Bin/msw/wx_${proj}.vcxproj", \&update_msbuild, @args);
 | |
|             call_upmake("$Bin/msw/wx_${proj}.vcxproj.filters", \&update_msbuild_filters, @args);
 | |
|         }
 | |
| 
 | |
|         # Pre-MSBuild projects also put this header together with all the other
 | |
|         # ones, so it needs to be added (with MSBuild it's a <CustomBuild>
 | |
|         # element and so is completely separate from the real headers that use
 | |
|         # <ClInclude>).
 | |
|         push @headers, "${top_srcdir}include/wx/msw/genrcdefs.h";
 | |
| 
 | |
|         # And core project also includes all GUI headers instead of just those
 | |
|         # it really uses.
 | |
|         if ($proj eq 'core') {
 | |
|             foreach my $gui_proj (grep { $_ ne 'base' &&
 | |
|                                          $_ ne 'core' &&
 | |
|                                          $_ ne 'net' &&
 | |
|                                          $_ ne 'xml' } keys %projects_vars) {
 | |
|                 foreach my $var (@{$projects_vars{$gui_proj}}) {
 | |
|                     push @headers, "${top_srcdir}include/$_" for @{$vars->{"${var}_HDR"}};
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # For compatibility with the existing bakefile-generated projects,
 | |
|         # don't include *.cpp files in the list of headers -- even these files
 | |
|         # are actually used as headers (i.e. they are #include'd).
 | |
|         if ($proj eq 'base') {
 | |
|             @headers = grep { $_ !~ /\.cpp$/ } @headers;
 | |
|         }
 | |
| 
 | |
|         foreach my $ver (@vcproj_versions) {
 | |
|             next if defined $only_version && $ver != $only_version;
 | |
| 
 | |
|             call_upmake("$Bin/msw/wx_vc${ver}_${proj}.vcproj", \&update_vcproj, @args);
 | |
|         }
 | |
|     }
 | |
| }
 |