diff --git a/.gitattributes b/.gitattributes
index ce3d889b3b..fd833611ca 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -33,3 +33,6 @@ config.guess eol=lf
config.sub eol=lf
configure eol=lf
configure.in eol=lf
+
+# Ignore changes in the generated files.
+build/upmake -diff
diff --git a/build/bakefiles/Bakefiles.bkgen b/build/bakefiles/Bakefiles.bkgen
index fe3aed1634..f0bcd7141d 100644
--- a/build/bakefiles/Bakefiles.bkgen
+++ b/build/bakefiles/Bakefiles.bkgen
@@ -30,6 +30,12 @@
autoconf,borland,dmars_smake,dmars,mingw,msvc,msvc6prj,msevc4prj,msvs2003prj,msvs2005prj,msvs2008prj,watcom
+
+
+ msvs2003prj,msvs2005prj,msvs2008prj
+
+
autoconf,msvc6prj,msevc4prj,msvs2003prj,msvs2005prj,msvs2008prj
diff --git a/build/msw/wx_core.vcxproj b/build/msw/wx_core.vcxproj
index 86d5b729cd..e2a32a8e80 100644
--- a/build/msw/wx_core.vcxproj
+++ b/build/msw/wx_core.vcxproj
@@ -901,6 +901,7 @@
$(IntDir)msw_%(Filename).obj
+
$(IntDir)msw_%(Filename).obj
@@ -1004,7 +1005,6 @@
-
@@ -1014,7 +1014,6 @@
true
true
-
@@ -1335,6 +1334,7 @@
+
@@ -1375,7 +1375,6 @@
-
diff --git a/build/msw/wx_html.vcxproj b/build/msw/wx_html.vcxproj
index df3a22b264..5d421cb9fe 100644
--- a/build/msw/wx_html.vcxproj
+++ b/build/msw/wx_html.vcxproj
@@ -482,6 +482,7 @@
+
diff --git a/build/msw/wx_html.vcxproj.filters b/build/msw/wx_html.vcxproj.filters
index 5284e0aac3..4249c9751a 100644
--- a/build/msw/wx_html.vcxproj.filters
+++ b/build/msw/wx_html.vcxproj.filters
@@ -33,6 +33,9 @@
Generic Sources
+
+ wxHTML Sources
+
wxHTML Sources
diff --git a/build/tools/upmake/lib/Text/Upmake.pm b/build/tools/upmake/lib/Text/Upmake.pm
deleted file mode 100644
index 2df09b5936..0000000000
--- a/build/tools/upmake/lib/Text/Upmake.pm
+++ /dev/null
@@ -1,78 +0,0 @@
-package Text::Upmake;
-
-use strict;
-use warnings;
-use autodie;
-
-use Exporter qw(import);
-
-our @EXPORT = qw(read_files_list upmake);
-
-=head1 NAME
-
-Text::Upmake - Update make files.
-
-=head1 SYNOPSIS
-
-=head1 AUTHOR
-
-Vadim Zeitlin
-
-=cut
-
-# 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.
-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;
- push @{$vars{$var}}, $_;
- }
- }
-
- return \%vars;
-}
-
-# Update the file with the given name in place using the specified function and
-# passing it the rest of the arguments.
-#
-# This is meant to be used with update_xxx() below.
-sub upmake
-{
- my ($fname, $updater, @args) = @_;
-
- 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;
- }
-
- $changed
-}
-
-1;
diff --git a/build/tools/upmake/lib/Text/Upmake/Bakefile0.pm b/build/tools/upmake/lib/Text/Upmake/Bakefile0.pm
deleted file mode 100644
index 3ee1c55e1c..0000000000
--- a/build/tools/upmake/lib/Text/Upmake/Bakefile0.pm
+++ /dev/null
@@ -1,97 +0,0 @@
-package Text::Upmake::Bakefile0;
-
-use Exporter qw(import);
-our @EXPORT = qw(update_bakefile_0);
-
-=head1 NAME
-
-Text::Upmake::Bakefile0 - Update bakefile-0.x files list.
-
-=head1 SYNOPSIS
-
-This is used exclusively to update wxWidgets C and is probably not
-useful outside of wxWidgets project.
-
- use Text::Upmake::Bakefile0;
- Text::Upmake::upmake('bakefiles/files.bkl', \&update_bakefile_0, $vars);
-
-=head1 SEE ALSO
-
-Text::Upmake
-
-=head1 AUTHOR
-
-Vadim Zeitlin
-
-=cut
-
-# 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.
-#
-# The caller must take care of actually renaming the second file to the first
-# one.
-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 (// && exists $vars->{$1}) {
- $var = $1;
- %files = map { $_ => 0 } @{$vars->{$var}};
- } elsif (defined $var) {
- local $_ = $_;
- s///;
- s/^\s+//;
- s/\s+$//;
- if (m{}) {
- # 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
-}
diff --git a/build/tools/upmake/lib/Text/Upmake/MSBuild.pm b/build/tools/upmake/lib/Text/Upmake/MSBuild.pm
deleted file mode 100644
index 9ec1120866..0000000000
--- a/build/tools/upmake/lib/Text/Upmake/MSBuild.pm
+++ /dev/null
@@ -1,252 +0,0 @@
-package Text::Upmake::MSBuild;
-
-use Exporter qw(import);
-our @EXPORT = qw(update_msbuild update_msbuild_filters);
-
-=head1 NAME
-
-Text::Upmake::MSBuild - Update list of sources and headers in MSBuild projects.
-
-=head1 SYNOPSIS
-
-Given an MSBuild project C and its associated filters file
-C, the functions in this module can be used to update
-the list of files in them to correspond to the given ones.
-
- use Text::Upmake::Bakefile0;
- Text::Upmake::upmake('projects.vcxproj', \&update_msbuild, \@sources, \@headers);
- Text::Upmake::upmake('projects.vcxproj.filters', \&update_msbuild_filters, \@sources, \@headers);
-
-=head1 SEE ALSO
-
-Text::Upmake
-
-=head1 AUTHOR
-
-Vadim Zeitlin
-
-=cut
-
-# 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.
-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 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*$/) {
- $in_group = 1;
- } elsif ($line =~ m{^\s*$}) {
- 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{ \n};
-
- $changed = 1;
- }
- }
-
- $in_sources = $in_headers = 0;
- }
-
- $in_group = 0;
- } elsif ($in_group) {
- if ($line =~ m{^\s*Compile|Include) Include="(?[^"]+)"\s*(?/)?>$}) {
- 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 tag, ignore
- # everything until the next
- while (<$in>) {
- last if m{^\s*$};
- }
- }
-
- # In any case skip either this line containing the full
- # 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
-}
-
-# 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.
-sub update_msbuild_filters
-{
- my ($in, $out, $sources, $headers, $filter_cb) = @_;
-
- # 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 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*?$/) {
- $in_group = 1;
- } elsif ($line =~ m{^\s*?$}) {
- 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 = defined $filter_cb ? $filter_cb->($file) : undef;
-
- # Convert path separator to the one used by MSBuild.
- $file =~ s@/@\\@g;
-
- my $indent = ' ' x 2;
-
- print $out qq{$indent$indent\n$indent$indent$indent$filter\n$indent$indent\n";
- } else {
- print $out " />\n";
- }
-
- $changed = 1;
- }
- }
-
- $in_sources = $in_headers = 0;
- $files = undef;
- }
-
- $in_group = 0;
- } elsif ($in_group &&
- $line =~ m{^\s*Compile|Include) Include="(?[^"]+)"\s*(?/)?>?$}) {
- 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 tag, get everything
- # until the next .
- while (<$in>) {
- $text .= $_;
- last if m{^\s*\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
-}
diff --git a/build/tools/upmake/t/01_read_files_list.t b/build/tools/upmake/t/01_read_files_list.t
deleted file mode 100644
index 042232be92..0000000000
--- a/build/tools/upmake/t/01_read_files_list.t
+++ /dev/null
@@ -1,24 +0,0 @@
-use strict;
-use warnings;
-use autodie;
-use Test::More;
-
-BEGIN { use_ok('Text::Upmake'); }
-
-my $vars = read_files_list(*DATA);
-is_deeply($vars->{VAR1}, [qw(file1 file2)], 'VAR1 has expected value');
-is_deeply($vars->{VAR2}, [qw(file3 file4)], 'VAR2 has expected value');
-
-done_testing()
-
-__DATA__
-# Some comments
-
-VAR1 =
- file1
- # comment between the files
- file2
-VAR2 =
- file3
- file4 # comment
- # another comment
diff --git a/build/tools/upmake/t/02_update_bakefile_0.t b/build/tools/upmake/t/02_update_bakefile_0.t
deleted file mode 100644
index 76957bf2b7..0000000000
--- a/build/tools/upmake/t/02_update_bakefile_0.t
+++ /dev/null
@@ -1,47 +0,0 @@
-use strict;
-use warnings;
-use autodie;
-use Test::More;
-
-BEGIN { use_ok('Text::Upmake::Bakefile0'); }
-
-my $vars = {
- VAR1 => [qw(file1 file2 fileNew)],
- VAR2 => [qw(file3 file4 file5 fileNew2)],
- };
-
-open my $out, '>', \my $outstr;
-update_bakefile_0(*DATA, $out, $vars);
-
-note("Result: $outstr");
-
-like($outstr, qr/file1/, 'existing file was preserved');
-like($outstr, qr/fileNew$/m, 'new file was added');
-unlike($outstr, qr/fileOld/, 'old file was removed');
-like($outstr, qr/fileNew2/, 'another new file was added');
-like($outstr, qr/file3\s+file4/s, 'files remain in correct order');
-
-done_testing()
-
-__DATA__
-
-
-
-
-
-
- file1
-
- file2
-
-
-
- file3
- file4
- file5
- fileOld
-
-
-
diff --git a/build/tools/upmake/t/03_update_msbuild.t b/build/tools/upmake/t/03_update_msbuild.t
deleted file mode 100644
index ceda9ceb65..0000000000
--- a/build/tools/upmake/t/03_update_msbuild.t
+++ /dev/null
@@ -1,50 +0,0 @@
-use strict;
-use warnings;
-use autodie;
-use Test::More;
-
-use Text::Upmake;
-BEGIN { use_ok('Text::Upmake::MSBuild'); }
-
-my $sources = [qw(file1.cpp file2.cpp fileNew.cpp)];
-my $headers = [qw(file1.h file2.h fileNew.h)];
-
-open my $out, '>', \my $outstr;
-update_msbuild(*DATA, $out, $sources, $headers);
-
-note("Result: $outstr");
-
-like($outstr, qr/file1\.cpp/, 'existing source file was preserved');
-like($outstr, qr/fileNew\.cpp/m, 'new source file was added');
-unlike($outstr, qr/fileOld\.cpp/, 'old source file was removed');
-unlike($outstr, qr/file3\.h/, 'old header was removed');
-like($outstr, qr/fileNew\.h/, 'new header was added');
-
-done_testing()
-
-__DATA__
-
-
-
-
- Debug
- Win32
-
-
-
-
-
-
-
- Create
-
-
-
-
-
-
-
-
-
-
-
diff --git a/build/tools/upmake/t/04_update_msbuild_filters.t b/build/tools/upmake/t/04_update_msbuild_filters.t
deleted file mode 100644
index 3dc21c6e58..0000000000
--- a/build/tools/upmake/t/04_update_msbuild_filters.t
+++ /dev/null
@@ -1,67 +0,0 @@
-use strict;
-use warnings;
-use autodie;
-use Test::More;
-
-use Text::Upmake;
-BEGIN { use_ok('Text::Upmake::MSBuild'); }
-
-my $sources = [qw(file1.cpp file2.cpp file4.cpp fileNew.cpp)];
-my $headers = [qw(file1.h file2.h fileNew.h)];
-
-sub filter_cb
-{
- my ($file) = @_;
-
- return 'New Sources' if $file =~ /New\./;
-
- undef
-}
-
-open my $out, '>', \my $outstr;
-update_msbuild_filters(*DATA, $out, $sources, $headers, \&filter_cb);
-
-note("Result: $outstr");
-
-like($outstr, qr/file1\.cpp/, 'existing source file was preserved');
-like($outstr, qr/fileNew\.cpp/m, 'new source file was added');
-unlike($outstr, qr/fileOld\.cpp/, 'old source file was removed');
-unlike($outstr, qr/file3\.cpp/, 'another old source file was removed');
-unlike($outstr, qr/file3\.h/, 'old header was removed');
-like($outstr, qr/fileNew\.h/, 'new header was added');
-
-done_testing()
-
-__DATA__
-
-
-
-
- {...}
-
-
- {...}
-
-
-
-
- Common Sources
-
-
- Common Sources
-
-
-
-
-
-
- Common Headers
-
-
- Common Headers
-
-
- Common Headers
-
-
-
diff --git a/build/upmake b/build/upmake
index 5824a95653..86f1cc7b81 100755
--- a/build/upmake
+++ b/build/upmake
@@ -1,5 +1,1345 @@
#!/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);
+
+ our $VERSION = '0.3'; # VERSION
+
+
+
+ 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;
+ }
+
+
+ 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;
+
+ __END__
+
+ =pod
+
+ =encoding UTF-8
+
+ =head1 NAME
+
+ Makefile::Update - Update make files.
+
+ =head1 VERSION
+
+ version 0.3
+
+ =head1 SYNOPSIS
+
+ use Makefile::Update;
+ my $vars = read_files_list('files.lst');
+ upmake('foo.vcxproj', $vars->{sources}, $vars->{headers});
+
+ =head1 FUNCTIONS
+
+ =head2 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
+
+ =head2 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
+
+ The path to the file to be updated, required.
+
+ =item C
+
+ If true, give more messages about what is being done.
+
+ =item C
+
+ If true, don't output any non-error messages.
+
+ =item C
+
+ If true, don't really update the file but just output whether it would have
+ been updated or not. If C is also true, also output the diff of the
+ changes that would have been done.
+
+ =back
+
+ This is meant to be used with C defined in different
+ Makefile::Update::Xxx modules.
+
+ Returns 1 if the file was changed or 0 otherwise.
+
+ =head1 AUTHOR
+
+ Vadim Zeitlin
+
+ =head1 COPYRIGHT AND LICENSE
+
+ This software is copyright (c) 2015 by Vadim Zeitlin.
+
+ This is free software; you can redistribute it and/or modify it under
+ the same terms as the Perl 5 programming language system itself.
+
+ =cut
+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;
+
+ our $VERSION = '0.3'; # VERSION
+
+
+
+ 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 (// && exists $vars->{$1}) {
+ $var = $1;
+ %files = map { $_ => 0 } @{$vars->{$var}};
+ } elsif (defined $var) {
+ local $_ = $_;
+ s///;
+ s/^\s+//;
+ s/\s+$//;
+ if (m{}) {
+ # 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
+ }
+
+ __END__
+
+ =pod
+
+ =encoding UTF-8
+
+ =head1 NAME
+
+ Makefile::Update::Bakefile0 - Update bakefile-0.x files list.
+
+ =head1 VERSION
+
+ version 0.3
+
+ =head1 SYNOPSIS
+
+ This is used exclusively to update wxWidgets C and is probably not
+ useful outside of wxWidgets project.
+
+ use Makefile::Update::Bakefile0;
+ Makefile::Update::upmake('bakefiles/files.bkl', \&update_bakefile_0, $vars);
+
+ =head1 FUNCTIONS
+
+ =head2 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.
+
+ =head1 SEE ALSO
+
+ Makefile::Update
+
+ =head1 AUTHOR
+
+ Vadim Zeitlin
+
+ =head1 COPYRIGHT AND LICENSE
+
+ This software is copyright (c) 2015 by Vadim Zeitlin.
+
+ This is free software; you can redistribute it and/or modify it under
+ the same terms as the Perl 5 programming language system itself.
+
+ =cut
+MAKEFILE_UPDATE_BAKEFILE0
+
+$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;
+
+ our $VERSION = '0.3'; # VERSION
+
+
+
+ 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
+ );
+ }
+
+
+
+ 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 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*$/) {
+ $in_group = 1;
+ } elsif ($line =~ m{^\s*$}) {
+ 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{ \r\n};
+
+ $changed = 1;
+ }
+ }
+
+ $in_sources = $in_headers = 0;
+ $files = undef;
+ }
+
+ $in_group = 0;
+ } elsif ($in_group) {
+ if ($line =~ m{^\s*Compile|Include) Include="(?[^"]+)"\s*(?/)?>$}) {
+ 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 or
+ # 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
+ # 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
+ }
+
+
+ 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 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*?$/) {
+ $in_group = 1;
+ } elsif ($line =~ m{^\s*?$}) {
+ 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\r\n$indent$indent$indent$filter\r\n$indent$indent\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*Compile|Include) Include="(?[^"]+)"\s*(?/)?>?$}) {
+ 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 tag, get everything
+ # until the next .
+ while (<$in>) {
+ $text .= $_;
+ last if m{^\s*\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
+ }
+
+ __END__
+
+ =pod
+
+ =encoding UTF-8
+
+ =head1 NAME
+
+ Makefile::Update::MSBuild - Update list of sources and headers in MSBuild projects.
+
+ =head1 VERSION
+
+ version 0.3
+
+ =head1 SYNOPSIS
+
+ Given an MSBuild project C and its associated filters file
+ C, 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 FUNCTIONS
+
+ =head2 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 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.
+
+ =head2 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.
+
+ =head2 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.
+
+ =head1 SEE ALSO
+
+ Makefile::Update, Makefile::Update::VCProj
+
+ =head1 AUTHOR
+
+ Vadim Zeitlin
+
+ =head1 COPYRIGHT AND LICENSE
+
+ This software is copyright (c) 2015 by Vadim Zeitlin.
+
+ This is free software; you can redistribute it and/or modify it under
+ the same terms as the Perl 5 programming language system itself.
+
+ =cut
+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;
+
+ our $VERSION = '0.3'; # VERSION
+
+
+
+ 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 =~ /^(?\s*)(?[^ ]+)(?\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*(?\S+)\s*(?::?=|:)(?[^=]*)$/) {
+ $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
+ }
+
+ __END__
+
+ =pod
+
+ =encoding UTF-8
+
+ =head1 NAME
+
+ Makefile::Update::Makefile - Update lists of files in makefile variables.
+
+ =head1 VERSION
+
+ version 0.3
+
+ =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 FUNCTIONS
+
+ =head2 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.
+
+ =head1 SEE ALSO
+
+ Makefile::Update
+
+ =head1 AUTHOR
+
+ Vadim Zeitlin
+
+ =head1 COPYRIGHT AND LICENSE
+
+ This software is copyright (c) 2015 by Vadim Zeitlin.
+
+ This is free software; you can redistribute it and/or modify it under
+ the same terms as the Perl 5 programming language system itself.
+
+ =cut
+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;
+
+ our $VERSION = '0.3'; # VERSION
+
+
+
+ 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 "" 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* 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 tag at line $..\n};
+ }
+ } elsif (defined $filter) {
+ if ($line =~ /^\s*;
+ 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 tag in any case.
+ while (<$in>) {
+ last if qr{^\s*\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 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*$}) {
+ 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
+ ;
+
+ $changed = 1;
+ }
+ }
+
+ undef $filter;
+ }
+ }
+
+ print $out $line_with_eol;
+ }
+
+ $changed
+ }
+
+ __END__
+
+ =pod
+
+ =encoding UTF-8
+
+ =head1 NAME
+
+ Makefile::Update::VCProj - Update list of sources and headers in Visual C++ projects.
+
+ =head1 VERSION
+
+ version 0.3
+
+ =head1 SYNOPSIS
+
+ The function L can be used to update the list of headers and
+ sources in the given Visual C++ project file C:
+
+ use Makefile::Update::VCProj;
+ upmake_msbuild_project('project.vcproj', \@sources, \@headers);
+
+ =head1 FUNCTIONS
+
+ =head2 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.
+
+ =head1 SEE ALSO
+
+ Makefile::Update, Makefile::Update::MSBuild
+
+ =head1 AUTHOR
+
+ Vadim Zeitlin
+
+ =head1 COPYRIGHT AND LICENSE
+
+ This software is copyright (c) 2015 by Vadim Zeitlin.
+
+ This is free software; you can redistribute it and/or modify it under
+ the same terms as the Perl 5 programming language system itself.
+
+ =cut
+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]}) {
+ return sub {
+ return 0 unless length $fat;
+ $fat =~ s/^([^\n]*\n?)//;
+ $_ = $1;
+ return 1;
+ };
+ }
+ return;
+ };
+}
+
+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;
@@ -7,55 +1347,52 @@ use autodie;
use Getopt::Long;
use FindBin qw($Bin);
-use lib "$Bin/tools/upmake/lib";
-use Text::Upmake;
-use Text::Upmake::Bakefile0;
-use Text::Upmake::MSBuild;
+use Makefile::Update;
+use Makefile::Update::Bakefile0;
+use Makefile::Update::MSBuild;
+use Makefile::Update::VCProj;
my $verbose = 0;
my $quiet = 0;
-my ($only_bkl, $only_msbuild, $only_project, $only_version);
+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_msbuild = 1; $only_project = $_[1] },
- 'only-version=i' => sub { $only_msbuild = 1; $only_version = $_[1] },
+ 'only-project=s' => sub { $only_msvs = 1; $only_project = $_[1] },
+ 'only-version=i' => sub { $only_msvs = 1; $only_version = $_[1] },
) or die <]
+Usage: $0 [--verbose] [--quiet] [--only-bkl] [--only-project=] [--only-version=]
-Update the files used by bakefile and MSBuild projects from the master list
+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_msbuild) {
+if ($only_bkl && $only_msvs) {
die qq{Options --only-bkl and --only-project or --only-version can't be used together.\n}
}
-sub log_upmake
+sub call_upmake
{
my ($fname, @args) = @_;
- if (upmake($fname, @args)) {
- 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;
- }
+ upmake({file => $fname, quiet => $quiet, verbose => $verbose}, @args)
}
open my $files, '<', "$Bin/files";
my $vars = read_files_list($files);
-if (!$only_msbuild) {
- if (log_upmake("$Bin/bakefiles/files.bkl", \&update_bakefile_0, $vars)) {
+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".} if $verbose;
}
}
@@ -65,7 +1402,7 @@ if (!$only_bkl) {
# projects.
my $top_srcdir = '../../';
- # The base names of all our MSBuild projects with the list of variables
+ # 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)],
@@ -79,12 +1416,16 @@ if (!$only_bkl) {
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
{
@@ -143,7 +1484,42 @@ if (!$only_bkl) {
my @args = (\@sources, \@headers, \&filter_cb);
- log_upmake("$Bin/msw/wx_${proj}.vcxproj", \&update_msbuild, @args);
- log_upmake("$Bin/msw/wx_${proj}.vcxproj.filters", \&update_msbuild_filters, @args);
+ # 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
+ # element and so is completely separate from the real headers that use
+ # ).
+ 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);
+ }
}
}
diff --git a/build/upmake_script.pl b/build/upmake_script.pl
new file mode 100755
index 0000000000..8f06ac7dcb
--- /dev/null
+++ b/build/upmake_script.pl
@@ -0,0 +1,185 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use autodie;
+
+use Getopt::Long;
+
+use FindBin qw($Bin);
+
+use Makefile::Update;
+use Makefile::Update::Bakefile0;
+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 <] [--only-version=]
+
+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".} if $verbose;
+ }
+}
+
+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)],
+ 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
+ # element and so is completely separate from the real headers that use
+ # ).
+ 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);
+ }
+ }
+}