123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- #!/usr/bin/perl -w
- # Add source files and headers to Xcode and Visual Studio projects.
- # THIS IS NOT ROBUST, THIS IS JUST RYAN AVOIDING RUNNING BETWEEN
- # THREE COMPUTERS AND A BUNCH OF DEVELOPMENT ENVIRONMENTS TO ADD
- # A STUPID FILE TO THE BUILD.
- use warnings;
- use strict;
- use File::Basename;
- my %xcode_references = ();
- sub generate_xcode_id {
- my @chars = ('0'..'9', 'A'..'F');
- my $str;
- do {
- my $len = 16;
- $str = '0000'; # start and end with '0000' so we know we added it.
- while ($len--) {
- $str .= $chars[rand @chars]
- };
- $str .= '0000'; # start and end with '0000' so we know we added it.
- } while (defined($xcode_references{$str}));
- $xcode_references{$str} = 1; # so future calls can't generate this one.
- return $str;
- }
- sub process_xcode {
- my $addpath = shift;
- my $pbxprojfname = shift;
- my $lineno;
- %xcode_references = ();
- my $addfname = basename($addpath);
- my $addext = '';
- if ($addfname =~ /\.(.*?)\Z/) {
- $addext = $1;
- }
- my $is_public_header = ($addpath =~ /\Ainclude\/SDL3\//) ? 1 : 0;
- my $filerefpath = $is_public_header ? "SDL3/$addfname" : $addfname;
- my $srcs_or_headers = '';
- my $addfiletype = '';
- if ($addext eq 'c') {
- $srcs_or_headers = 'Sources';
- $addfiletype = 'sourcecode.c.c';
- } elsif ($addext eq 'm') {
- $srcs_or_headers = 'Sources';
- $addfiletype = 'sourcecode.c.objc';
- } elsif ($addext eq 'h') {
- $srcs_or_headers = 'Headers';
- $addfiletype = 'sourcecode.c.h';
- } else {
- die("Unexpected file extension '$addext'\n");
- }
- my $fh;
- open $fh, '<', $pbxprojfname or die("Failed to open '$pbxprojfname': $!\n");
- chomp(my @pbxproj = <$fh>);
- close($fh);
- # build a table of all ids, in case we duplicate one by some miracle.
- $lineno = 0;
- foreach (@pbxproj) {
- $lineno++;
- # like "F3676F582A7885080091160D /* SDL3.dmg */ = {"
- if (/\A\t\t([A-F0-9]{24}) \/\* (.*?) \*\/ \= \{\Z/) {
- $xcode_references{$1} = $2;
- }
- }
- # build out of a tree of PBXGroup items.
- my %pbxgroups = ();
- my $thispbxgroup;
- my $pbxgroup_children;
- my $pbxgroup_state = 0;
- my $pubheaders_group_hash = '';
- my $libsrc_group_hash = '';
- $lineno = 0;
- foreach (@pbxproj) {
- $lineno++;
- if ($pbxgroup_state == 0) {
- $pbxgroup_state++ if /\A\/\* Begin PBXGroup section \*\/\Z/;
- } elsif ($pbxgroup_state == 1) {
- # like "F3676F582A7885080091160D /* SDL3.dmg */ = {"
- if (/\A\t\t([A-F0-9]{24}) \/\* (.*?) \*\/ \= \{\Z/) {
- my %newhash = ();
- $pbxgroups{$1} = \%newhash;
- $thispbxgroup = \%newhash;
- $pubheaders_group_hash = $1 if $2 eq 'Public Headers';
- $libsrc_group_hash = $1 if $2 eq 'Library Source';
- $pbxgroup_state++;
- } elsif (/\A\/\* End PBXGroup section \*\/\Z/) {
- last;
- } else {
- die("Expected pbxgroup obj on '$pbxprojfname' line $lineno\n");
- }
- } elsif ($pbxgroup_state == 2) {
- if (/\A\t\t\tisa \= PBXGroup;\Z/) {
- $pbxgroup_state++;
- } else {
- die("Expected pbxgroup obj's isa field on '$pbxprojfname' line $lineno\n");
- }
- } elsif ($pbxgroup_state == 3) {
- if (/\A\t\t\tchildren \= \(\Z/) {
- my %newhash = ();
- $$thispbxgroup{'children'} = \%newhash;
- $pbxgroup_children = \%newhash;
- $pbxgroup_state++;
- } else {
- die("Expected pbxgroup obj's children field on '$pbxprojfname' line $lineno\n");
- }
- } elsif ($pbxgroup_state == 4) {
- if (/\A\t\t\t\t([A-F0-9]{24}) \/\* (.*?) \*\/,\Z/) {
- $$pbxgroup_children{$1} = $2;
- } elsif (/\A\t\t\t\);\Z/) {
- $pbxgroup_state++;
- } else {
- die("Expected pbxgroup obj's children element on '$pbxprojfname' line $lineno\n");
- }
- } elsif ($pbxgroup_state == 5) {
- if (/\A\t\t\t(.*?) \= (.*?);\Z/) {
- $$thispbxgroup{$1} = $2;
- } elsif (/\A\t\t\};\Z/) {
- $pbxgroup_state = 1;
- } else {
- die("Expected pbxgroup obj field on '$pbxprojfname' line $lineno\n");
- }
- } else {
- die("bug in this script.");
- }
- }
- die("Didn't see PBXGroup section in '$pbxprojfname'. Bug?\n") if $pbxgroup_state == 0;
- die("Didn't see Public Headers PBXGroup in '$pbxprojfname'. Bug?\n") if $pubheaders_group_hash eq '';
- die("Didn't see Library Source PBXGroup in '$pbxprojfname'. Bug?\n") if $libsrc_group_hash eq '';
- # Some debug log dumping...
- if (0) {
- foreach (keys %pbxgroups) {
- my $k = $_;
- my $g = $pbxgroups{$k};
- print("$_:\n");
- foreach (keys %$g) {
- print(" $_:\n");
- if ($_ eq 'children') {
- my $kids = $$g{$_};
- foreach (keys %$kids) {
- print(" $_ -> " . $$kids{$_} . "\n");
- }
- } else {
- print(' ' . $$g{$_} . "\n");
- }
- }
- print("\n");
- }
- }
- # Get some unique IDs for our new thing.
- my $fileref = generate_xcode_id();
- my $buildfileref = generate_xcode_id();
- # Figure out what group to insert this into (or what groups to make)
- my $add_to_group_fileref = $fileref;
- my $add_to_group_addfname = $addfname;
- my $newgrptext = '';
- my $grphash = '';
- if ($is_public_header) {
- $grphash = $pubheaders_group_hash; # done!
- } else {
- $grphash = $libsrc_group_hash;
- my @splitpath = split(/\//, dirname($addpath));
- if ($splitpath[0] eq 'src') {
- shift @splitpath;
- }
- while (my $elem = shift(@splitpath)) {
- my $g = $pbxgroups{$grphash};
- my $kids = $$g{'children'};
- my $found = 0;
- foreach (keys %$kids) {
- my $hash = $_;
- my $fname = $$kids{$hash};
- if (uc($fname) eq uc($elem)) {
- $grphash = $hash;
- $found = 1;
- last;
- }
- }
- unshift(@splitpath, $elem), last if (not $found);
- }
- if (@splitpath) { # still elements? We need to build groups.
- my $newgroupref = generate_xcode_id();
- $add_to_group_fileref = $newgroupref;
- $add_to_group_addfname = $splitpath[0];
- while (my $elem = shift(@splitpath)) {
- my $lastelem = @splitpath ? 0 : 1;
- my $childhash = $lastelem ? $fileref : generate_xcode_id();
- my $childpath = $lastelem ? $addfname : $splitpath[0];
- $newgrptext .= "\t\t$newgroupref /* $elem */ = {\n";
- $newgrptext .= "\t\t\tisa = PBXGroup;\n";
- $newgrptext .= "\t\t\tchildren = (\n";
- $newgrptext .= "\t\t\t\t$childhash /* $childpath */,\n";
- $newgrptext .= "\t\t\t);\n";
- $newgrptext .= "\t\t\tpath = $elem;\n";
- $newgrptext .= "\t\t\tsourceTree = \"<group>\";\n";
- $newgrptext .= "\t\t};\n";
- $newgroupref = $childhash;
- }
- }
- }
- my $tmpfname = "$pbxprojfname.tmp";
- open $fh, '>', $tmpfname or die("Failed to open '$tmpfname': $!\n");
- my $add_to_this_group = 0;
- $pbxgroup_state = 0;
- $lineno = 0;
- foreach (@pbxproj) {
- $lineno++;
- if ($pbxgroup_state == 0) {
- # Drop in new references at the end of their sections...
- if (/\A\/\* End PBXBuildFile section \*\/\Z/) {
- print $fh "\t\t$buildfileref /* $addfname in $srcs_or_headers */ = {isa = PBXBuildFile; fileRef = $fileref /* $addfname */;";
- if ($is_public_header) {
- print $fh " settings = {ATTRIBUTES = (Public, ); };";
- }
- print $fh " };\n";
- } elsif (/\A\/\* End PBXFileReference section \*\/\Z/) {
- print $fh "\t\t$fileref /* $addfname */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = $addfiletype; name = $addfname; path = $filerefpath; sourceTree = \"<group>\"; };\n";
- } elsif (/\A\/\* Begin PBXGroup section \*\/\Z/) {
- $pbxgroup_state = 1;
- } elsif (/\A\/\* Begin PBXSourcesBuildPhase section \*\/\Z/) {
- $pbxgroup_state = 5;
- }
- } elsif ($pbxgroup_state == 1) {
- if (/\A\t\t([A-F0-9]{24}) \/\* (.*?) \*\/ \= \{\Z/) {
- $pbxgroup_state++;
- $add_to_this_group = $1 eq $grphash;
- } elsif (/\A\/\* End PBXGroup section \*\/\Z/) {
- print $fh $newgrptext;
- $pbxgroup_state = 0;
- }
- } elsif ($pbxgroup_state == 2) {
- if (/\A\t\t\tchildren \= \(\Z/) {
- $pbxgroup_state++;
- }
- } elsif ($pbxgroup_state == 3) {
- if (/\A\t\t\t\);\Z/) {
- if ($add_to_this_group) {
- print $fh "\t\t\t\t$add_to_group_fileref /* $add_to_group_addfname */,\n";
- }
- $pbxgroup_state++;
- }
- } elsif ($pbxgroup_state == 4) {
- if (/\A\t\t\};\Z/) {
- $add_to_this_group = 0;
- }
- $pbxgroup_state = 1;
- } elsif ($pbxgroup_state == 5) {
- if (/\A\t\t\t\);\Z/) {
- if ($srcs_or_headers eq 'Sources') {
- print $fh "\t\t\t\t$buildfileref /* $addfname in $srcs_or_headers */,\n";
- }
- $pbxgroup_state = 0;
- }
- }
- print $fh "$_\n";
- }
- close($fh);
- rename($tmpfname, $pbxprojfname);
- }
- my %visualc_references = ();
- sub generate_visualc_id { # these are just standard Windows GUIDs.
- my @chars = ('0'..'9', 'a'..'f');
- my $str;
- do {
- my $len = 24;
- $str = '0000'; # start and end with '0000' so we know we added it.
- while ($len--) {
- $str .= $chars[rand @chars]
- };
- $str .= '0000'; # start and end with '0000' so we know we added it.
- $str =~ s/\A(........)(....)(....)(............)\Z/$1-$2-$3-$4/; # add dashes in the appropriate places.
- } while (defined($visualc_references{$str}));
- $visualc_references{$str} = 1; # so future calls can't generate this one.
- return $str;
- }
- sub process_visualstudio {
- my $addpath = shift;
- my $vcxprojfname = shift;
- my $lineno;
- %visualc_references = ();
- my $is_public_header = ($addpath =~ /\Ainclude\/SDL3\//) ? 1 : 0;
- my $addfname = basename($addpath);
- my $addext = '';
- if ($addfname =~ /\.(.*?)\Z/) {
- $addext = $1;
- }
- my $isheader = 0;
- if ($addext eq 'c') {
- $isheader = 0;
- } elsif ($addext eq 'm') {
- return; # don't add Objective-C files to Visual Studio projects!
- } elsif ($addext eq 'h') {
- $isheader = 1;
- } else {
- die("Unexpected file extension '$addext'\n");
- }
- my $fh;
- open $fh, '<', $vcxprojfname or die("Failed to open '$vcxprojfname': $!\n");
- chomp(my @vcxproj = <$fh>);
- close($fh);
- my $vcxgroup_state;
- my $rawaddvcxpath = "$addpath";
- $rawaddvcxpath =~ s/\//\\/g;
- # Figure out relative path from vcxproj file...
- my $addvcxpath = '';
- my @subdirs = split(/\//, $vcxprojfname);
- pop @subdirs;
- foreach (@subdirs) {
- $addvcxpath .= "..\\";
- }
- $addvcxpath .= $rawaddvcxpath;
- my $prevname = undef;
- my $tmpfname;
- $tmpfname = "$vcxprojfname.tmp";
- open $fh, '>', $tmpfname or die("Failed to open '$tmpfname': $!\n");
- my $added = 0;
- $added = 0;
- $vcxgroup_state = 0;
- $prevname = undef;
- $lineno = 0;
- foreach (@vcxproj) {
- $lineno++;
- if ($vcxgroup_state == 0) {
- if (/\A \<ItemGroup\>\Z/) {
- $vcxgroup_state = 1;
- $prevname = undef;
- }
- } elsif ($vcxgroup_state == 1) {
- if (/\A \<ClInclude .*\Z/) {
- $vcxgroup_state = 2 if $isheader;
- } elsif (/\A \<ClCompile .*\Z/) {
- $vcxgroup_state = 3 if not $isheader;
- } elsif (/\A \<\/ItemGroup\>\Z/) {
- $vcxgroup_state = 0;
- $prevname = undef;
- }
- }
- # Don't do elsif, we need to check this line again.
- if ($vcxgroup_state == 2) {
- if (/\A <ClInclude Include="(.*?)" \/\>\Z/) {
- my $nextname = $1;
- if ((not $added) && (((not defined $prevname) || (uc($prevname) lt uc($addvcxpath))) && (uc($nextname) gt uc($addvcxpath)))) {
- print $fh " <ClInclude Include=\"$addvcxpath\" />\n";
- $vcxgroup_state = 0;
- $added = 1;
- }
- $prevname = $nextname;
- } elsif (/\A \<\/ItemGroup\>\Z/) {
- if ((not $added) && ((not defined $prevname) || (uc($prevname) lt uc($addvcxpath)))) {
- print $fh " <ClInclude Include=\"$addvcxpath\" />\n";
- $vcxgroup_state = 0;
- $added = 1;
- }
- }
- } elsif ($vcxgroup_state == 3) {
- if (/\A <ClCompile Include="(.*?)" \/\>\Z/) {
- my $nextname = $1;
- if ((not $added) && (((not defined $prevname) || (uc($prevname) lt uc($addvcxpath))) && (uc($nextname) gt uc($addvcxpath)))) {
- print $fh " <ClCompile Include=\"$addvcxpath\" />\n";
- $vcxgroup_state = 0;
- $added = 1;
- }
- $prevname = $nextname;
- } elsif (/\A \<\/ItemGroup\>\Z/) {
- if ((not $added) && ((not defined $prevname) || (uc($prevname) lt uc($addvcxpath)))) {
- print $fh " <ClCompile Include=\"$addvcxpath\" />\n";
- $vcxgroup_state = 0;
- $added = 1;
- }
- }
- }
- print $fh "$_\n";
- }
- close($fh);
- rename($tmpfname, $vcxprojfname);
- my $vcxfiltersfname = "$vcxprojfname.filters";
- open $fh, '<', $vcxfiltersfname or die("Failed to open '$vcxfiltersfname': $!\n");
- chomp(my @vcxfilters = <$fh>);
- close($fh);
- my $newgrptext = '';
- my $filter = '';
- if ($is_public_header) {
- $filter = 'API Headers';
- } else {
- $filter = lc(dirname($addpath));
- $filter =~ s/\Asrc\///; # there's no filter for the base "src/" dir, where SDL.c and friends live.
- $filter =~ s/\//\\/g;
- $filter = '' if $filter eq 'src';
- if ($filter ne '') {
- # see if the filter already exists, otherwise add it.
- my %existing_filters = ();
- my $current_filter = '';
- my $found = 0;
- foreach (@vcxfilters) {
- # These lines happen to be unique, so we don't have to parse down to find this section.
- if (/\A \<Filter Include\="(.*?)"\>\Z/) {
- $current_filter = lc($1);
- if ($current_filter eq $filter) {
- $found = 1;
- }
- } elsif (/\A \<UniqueIdentifier\>\{(.*?)\}\<\/UniqueIdentifier\>\Z/) {
- $visualc_references{$1} = $current_filter; # gather up existing GUIDs to avoid duplicates.
- $existing_filters{$current_filter} = $1;
- }
- }
- if (not $found) { # didn't find it? We need to build filters.
- my $subpath = '';
- my @splitpath = split(/\\/, $filter);
- while (my $elem = shift(@splitpath)) {
- $subpath .= "\\" if ($subpath ne '');
- $subpath .= $elem;
- if (not $existing_filters{$subpath}) {
- my $newgroupref = generate_visualc_id();
- $newgrptext .= " <Filter Include=\"$subpath\">\n";
- $newgrptext .= " <UniqueIdentifier>{$newgroupref}</UniqueIdentifier>\n";
- $newgrptext .= " </Filter>\n"
- }
- }
- }
- }
- }
- $tmpfname = "$vcxfiltersfname.tmp";
- open $fh, '>', $tmpfname or die("Failed to open '$tmpfname': $!\n");
- $added = 0;
- $vcxgroup_state = 0;
- $prevname = undef;
- $lineno = 0;
- foreach (@vcxfilters) {
- $lineno++;
- # We cheat here, because these lines are unique, we don't have to fully parse this file.
- if ($vcxgroup_state == 0) {
- if (/\A \<Filter Include\="(.*?)"\>\Z/) {
- if ($newgrptext ne '') {
- $vcxgroup_state = 1;
- $prevname = undef;
- }
- } elsif (/\A \<ClInclude .*\Z/) {
- if ($isheader) {
- $vcxgroup_state = 2;
- $prevname = undef;
- }
- } elsif (/\A \<ClCompile .*\Z/) {
- if (not $isheader) {
- $vcxgroup_state = 3;
- $prevname = undef;
- }
- }
- }
- # Don't do elsif, we need to check this line again.
- if ($vcxgroup_state == 1) {
- if (/\A \<\/ItemGroup\>\Z/) {
- print $fh $newgrptext;
- $newgrptext = '';
- $vcxgroup_state = 0;
- }
- } elsif ($vcxgroup_state == 2) {
- if (/\A <ClInclude Include="(.*?)"/) {
- my $nextname = $1;
- if ((not $added) && (((not defined $prevname) || (uc($prevname) lt uc($addvcxpath))) && (uc($nextname) gt uc($addvcxpath)))) {
- print $fh " <ClInclude Include=\"$addvcxpath\"";
- if ($filter ne '') {
- print $fh ">\n";
- print $fh " <Filter>$filter</Filter>\n";
- print $fh " </ClInclude>\n";
- } else {
- print $fh " />\n";
- }
- $added = 1;
- }
- $prevname = $nextname;
- } elsif (/\A \<\/ItemGroup\>\Z/) {
- if ((not $added) && ((not defined $prevname) || (uc($prevname) lt uc($addvcxpath)))) {
- print $fh " <ClInclude Include=\"$addvcxpath\"";
- if ($filter ne '') {
- print $fh ">\n";
- print $fh " <Filter>$filter</Filter>\n";
- print $fh " </ClInclude>\n";
- } else {
- print $fh " />\n";
- }
- $added = 1;
- }
- $vcxgroup_state = 0;
- }
- } elsif ($vcxgroup_state == 3) {
- if (/\A <ClCompile Include="(.*?)"/) {
- my $nextname = $1;
- if ((not $added) && (((not defined $prevname) || (uc($prevname) lt uc($addvcxpath))) && (uc($nextname) gt uc($addvcxpath)))) {
- print $fh " <ClCompile Include=\"$addvcxpath\"";
- if ($filter ne '') {
- print $fh ">\n";
- print $fh " <Filter>$filter</Filter>\n";
- print $fh " </ClCompile>\n";
- } else {
- print $fh " />\n";
- }
- $added = 1;
- }
- $prevname = $nextname;
- } elsif (/\A \<\/ItemGroup\>\Z/) {
- if ((not $added) && ((not defined $prevname) || (uc($prevname) lt uc($addvcxpath)))) {
- print $fh " <ClCompile Include=\"$addvcxpath\"";
- if ($filter ne '') {
- print $fh ">\n";
- print $fh " <Filter>$filter</Filter>\n";
- print $fh " </ClCompile>\n";
- } else {
- print $fh " />\n";
- }
- $added = 1;
- }
- $vcxgroup_state = 0;
- }
- }
- print $fh "$_\n";
- }
- close($fh);
- rename($tmpfname, $vcxfiltersfname);
- }
- # Mainline!
- chdir(dirname($0)); # assumed to be in build-scripts
- chdir('..'); # head to root of source tree.
- foreach (@ARGV) {
- s/\A\.\///; # Turn "./path/to/file.txt" into "path/to/file.txt"
- my $arg = $_;
- process_xcode($arg, 'Xcode/SDL/SDL.xcodeproj/project.pbxproj');
- process_visualstudio($arg, 'VisualC/SDL/SDL.vcxproj');
- process_visualstudio($arg, 'VisualC-GDK/SDL/SDL.vcxproj');
- }
- print("Done. Please run `git diff` and make sure this looks okay!\n");
- exit(0);
|