]>
Commit | Line | Data |
---|---|---|
259d87c3 MSO |
1 | #!/usr/bin/perl -w |
2 | ###################################################################### | |
3 | # Do not call this script directly! | |
4 | # | |
5 | # The generate script ensures that @INC is correct before the engine | |
6 | # is executed. | |
7 | # | |
8 | # Copyright (C) 2009 Marius Storm-Olsen <mstormo@gmail.com> | |
9 | ###################################################################### | |
10 | use strict; | |
11 | use File::Basename; | |
12 | use File::Spec; | |
13 | use Cwd; | |
14 | use Generators; | |
d8c07013 | 15 | use Text::ParseWords; |
259d87c3 MSO |
16 | |
17 | my (%build_structure, %compile_options, @makedry); | |
18 | my $out_dir = getcwd(); | |
19 | my $git_dir = $out_dir; | |
20 | $git_dir =~ s=\\=/=g; | |
21 | $git_dir = dirname($git_dir) while (!-e "$git_dir/git.c" && "$git_dir" ne ""); | |
22 | die "Couldn't find Git repo" if ("$git_dir" eq ""); | |
23 | ||
24 | my @gens = Generators::available(); | |
25 | my $gen = "Vcproj"; | |
26 | ||
27 | sub showUsage | |
28 | { | |
29 | my $genlist = join(', ', @gens); | |
30 | print << "EOM"; | |
31 | generate usage: | |
32 | -g <GENERATOR> --gen <GENERATOR> Specify the buildsystem generator (default: $gen) | |
33 | Available: $genlist | |
34 | -o <PATH> --out <PATH> Specify output directory generation (default: .) | |
a530a59a | 35 | --make-out <PATH> Write the output of GNU Make into a file |
259d87c3 MSO |
36 | -i <FILE> --in <FILE> Specify input file, instead of running GNU Make |
37 | -h,-? --help This help | |
38 | EOM | |
39 | exit 0; | |
40 | } | |
41 | ||
42 | # Parse command-line options | |
a530a59a | 43 | my $make_out; |
259d87c3 MSO |
44 | while (@ARGV) { |
45 | my $arg = shift @ARGV; | |
46 | if ("$arg" eq "-h" || "$arg" eq "--help" || "$arg" eq "-?") { | |
47 | showUsage(); | |
48 | exit(0); | |
49 | } elsif("$arg" eq "--out" || "$arg" eq "-o") { | |
50 | $out_dir = shift @ARGV; | |
a530a59a PO |
51 | } elsif("$arg" eq "--make-out") { |
52 | $make_out = shift @ARGV; | |
259d87c3 MSO |
53 | } elsif("$arg" eq "--gen" || "$arg" eq "-g") { |
54 | $gen = shift @ARGV; | |
55 | } elsif("$arg" eq "--in" || "$arg" eq "-i") { | |
56 | my $infile = shift @ARGV; | |
57 | open(F, "<$infile") || die "Couldn't open file $infile"; | |
58 | @makedry = <F>; | |
59 | close(F); | |
66697467 JS |
60 | } else { |
61 | die "Unknown option: " . $arg; | |
259d87c3 MSO |
62 | } |
63 | } | |
64 | ||
65 | # NOT using File::Spec->rel2abs($path, $base) here, as | |
66 | # it fails badly for me in the msysgit environment | |
67 | $git_dir = File::Spec->rel2abs($git_dir); | |
68 | $out_dir = File::Spec->rel2abs($out_dir); | |
69 | my $rel_dir = makeOutRel2Git($git_dir, $out_dir); | |
70 | ||
71 | # Print some information so the user feels informed | |
72 | print << "EOM"; | |
73 | ----- | |
74 | Generator: $gen | |
75 | Git dir: $git_dir | |
76 | Out dir: $out_dir | |
77 | ----- | |
78 | Running GNU Make to figure out build structure... | |
79 | EOM | |
80 | ||
81 | # Pipe a make --dry-run into a variable, if not already loaded from file | |
03aa7118 PO |
82 | # Capture the make dry stderr to file for review (will be empty for a release build). |
83 | ||
84 | my $ErrsFile = "msvc-build-makedryerrors.txt"; | |
85 | @makedry = `make -C $git_dir -n MSVC=1 V=1 2>$ErrsFile` if !@makedry; | |
86 | # test for an empty Errors file and remove it | |
87 | unlink $ErrsFile if -f -z $ErrsFile; | |
259d87c3 | 88 | |
a530a59a PO |
89 | if (defined $make_out) { |
90 | open OUT, ">" . $make_out; | |
91 | print OUT @makedry; | |
92 | close OUT; | |
93 | } | |
94 | ||
259d87c3 MSO |
95 | # Parse the make output into usable info |
96 | parseMakeOutput(); | |
97 | ||
98 | # Finally, ask the generator to start generating.. | |
99 | Generators::generate($gen, $git_dir, $out_dir, $rel_dir, %build_structure); | |
100 | ||
101 | # main flow ends here | |
102 | # ------------------------------------------------------------------------------------------------- | |
103 | ||
104 | ||
105 | # 1) path: /foo/bar/baz 2) path: /foo/bar/baz 3) path: /foo/bar/baz | |
106 | # base: /foo/bar/baz/temp base: /foo/bar base: /tmp | |
107 | # rel: .. rel: baz rel: ../foo/bar/baz | |
108 | sub makeOutRel2Git | |
109 | { | |
110 | my ($path, $base) = @_; | |
111 | my $rel; | |
112 | if ("$path" eq "$base") { | |
113 | return "."; | |
114 | } elsif ($base =~ /^$path/) { | |
115 | # case 1 | |
116 | my $tmp = $base; | |
117 | $tmp =~ s/^$path//; | |
118 | foreach (split('/', $tmp)) { | |
119 | $rel .= "../" if ("$_" ne ""); | |
120 | } | |
121 | } elsif ($path =~ /^$base/) { | |
122 | # case 2 | |
123 | $rel = $path; | |
124 | $rel =~ s/^$base//; | |
125 | $rel = "./$rel"; | |
126 | } else { | |
127 | my $tmp = $base; | |
128 | foreach (split('/', $tmp)) { | |
129 | $rel .= "../" if ("$_" ne ""); | |
130 | } | |
131 | $rel .= $path; | |
132 | } | |
133 | $rel =~ s/\/\//\//g; # simplify | |
134 | $rel =~ s/\/$//; # don't end with / | |
135 | return $rel; | |
136 | } | |
137 | ||
138 | sub parseMakeOutput | |
139 | { | |
140 | print "Parsing GNU Make output to figure out build structure...\n"; | |
141 | my $line = 0; | |
142 | while (my $text = shift @makedry) { | |
143 | my $ate_next; | |
144 | do { | |
145 | $ate_next = 0; | |
146 | $line++; | |
147 | chomp $text; | |
148 | chop $text if ($text =~ /\r$/); | |
149 | if ($text =~ /\\$/) { | |
150 | $text =~ s/\\$//; | |
151 | $text .= shift @makedry; | |
152 | $ate_next = 1; | |
153 | } | |
154 | } while($ate_next); | |
155 | ||
74cf9bdd RJ |
156 | if ($text =~ /^test /) { |
157 | # options to test (eg -o) may be mistaken for linker options | |
158 | next; | |
159 | } | |
160 | ||
aae1713f PO |
161 | if ($text =~ /^(mkdir|msgfmt) /) { |
162 | # options to the Portable Object translations | |
163 | # the line "mkdir ... && msgfmt ..." contains no linker options | |
164 | next; | |
165 | } | |
166 | ||
259d87c3 MSO |
167 | if($text =~ / -c /) { |
168 | # compilation | |
169 | handleCompileLine($text, $line); | |
170 | ||
171 | } elsif ($text =~ / -o /) { | |
172 | # linking executable | |
173 | handleLinkLine($text, $line); | |
174 | ||
175 | } elsif ($text =~ /\.o / && $text =~ /\.a /) { | |
176 | # libifying | |
177 | handleLibLine($text, $line); | |
178 | # | |
179 | # } elsif ($text =~ /^cp /) { | |
180 | # # copy file around | |
181 | # | |
182 | # } elsif ($text =~ /^rm -f /) { | |
183 | # # shell command | |
184 | # | |
185 | # } elsif ($text =~ /^make[ \[]/) { | |
186 | # # make output | |
187 | # | |
188 | # } elsif ($text =~ /^echo /) { | |
189 | # # echo to file | |
190 | # | |
191 | # } elsif ($text =~ /^if /) { | |
192 | # # shell conditional | |
193 | # | |
194 | # } elsif ($text =~ /^tclsh /) { | |
195 | # # translation stuff | |
196 | # | |
197 | # } elsif ($text =~ /^umask /) { | |
198 | # # handling boilerplates | |
199 | # | |
200 | # } elsif ($text =~ /\$\(\:\)/) { | |
201 | # # ignore | |
202 | # | |
203 | # } elsif ($text =~ /^FLAGS=/) { | |
204 | # # flags check for dependencies | |
205 | # | |
206 | # } elsif ($text =~ /^'\/usr\/bin\/perl' -MError -e/) { | |
207 | # # perl commands for copying files | |
208 | # | |
209 | # } elsif ($text =~ /generate-cmdlist\.sh/) { | |
210 | # # command for generating list of commands | |
211 | # | |
259d87c3 MSO |
212 | # } elsif ($text =~ /new locations or Tcl/) { |
213 | # # command for detecting Tcl/Tk changes | |
214 | # | |
215 | # } elsif ($text =~ /mkdir -p/) { | |
216 | # # command creating path | |
217 | # | |
218 | # } elsif ($text =~ /: no custom templates yet/) { | |
219 | # # whatever | |
220 | # | |
221 | # } else { | |
222 | # print "Unhandled (line: $line): $text\n"; | |
223 | } | |
224 | } | |
225 | ||
226 | # use Data::Dumper; | |
227 | # print "Parsed build structure:\n"; | |
228 | # print Dumper(%build_structure); | |
229 | } | |
230 | ||
231 | # variables for the compilation part of each step | |
232 | my (@defines, @incpaths, @cflags, @sources); | |
233 | ||
234 | sub clearCompileStep | |
235 | { | |
236 | @defines = (); | |
237 | @incpaths = (); | |
238 | @cflags = (); | |
239 | @sources = (); | |
240 | } | |
241 | ||
242 | sub removeDuplicates | |
243 | { | |
244 | my (%dupHash, $entry); | |
245 | %dupHash = map { $_, 1 } @defines; | |
246 | @defines = keys %dupHash; | |
247 | ||
248 | %dupHash = map { $_, 1 } @incpaths; | |
249 | @incpaths = keys %dupHash; | |
250 | ||
251 | %dupHash = map { $_, 1 } @cflags; | |
252 | @cflags = keys %dupHash; | |
253 | } | |
254 | ||
255 | sub handleCompileLine | |
256 | { | |
257 | my ($line, $lineno) = @_; | |
d8c07013 | 258 | my @parts = shellwords($line); |
259d87c3 MSO |
259 | my $sourcefile; |
260 | shift(@parts); # ignore cmd | |
261 | while (my $part = shift @parts) { | |
262 | if ("$part" eq "-o") { | |
263 | # ignore object file | |
264 | shift @parts; | |
265 | } elsif ("$part" eq "-c") { | |
266 | # ignore compile flag | |
267 | } elsif ("$part" eq "-c") { | |
268 | } elsif ($part =~ /^.?-I/) { | |
269 | push(@incpaths, $part); | |
270 | } elsif ($part =~ /^.?-D/) { | |
271 | push(@defines, $part); | |
272 | } elsif ($part =~ /^-/) { | |
273 | push(@cflags, $part); | |
274 | } elsif ($part =~ /\.(c|cc|cpp)$/) { | |
275 | $sourcefile = $part; | |
276 | } else { | |
277 | die "Unhandled compiler option @ line $lineno: $part"; | |
278 | } | |
279 | } | |
280 | @{$compile_options{"${sourcefile}_CFLAGS"}} = @cflags; | |
281 | @{$compile_options{"${sourcefile}_DEFINES"}} = @defines; | |
282 | @{$compile_options{"${sourcefile}_INCPATHS"}} = @incpaths; | |
283 | clearCompileStep(); | |
284 | } | |
285 | ||
286 | sub handleLibLine | |
287 | { | |
288 | my ($line, $lineno) = @_; | |
289 | my (@objfiles, @lflags, $libout, $part); | |
290 | # kill cmd and rm 'prefix' | |
291 | $line =~ s/^rm -f .* && .* rcs //; | |
d8c07013 | 292 | my @parts = shellwords($line); |
259d87c3 MSO |
293 | while ($part = shift @parts) { |
294 | if ($part =~ /^-/) { | |
295 | push(@lflags, $part); | |
296 | } elsif ($part =~ /\.(o|obj)$/) { | |
297 | push(@objfiles, $part); | |
298 | } elsif ($part =~ /\.(a|lib)$/) { | |
299 | $libout = $part; | |
300 | $libout =~ s/\.a$//; | |
301 | } else { | |
302 | die "Unhandled lib option @ line $lineno: $part"; | |
303 | } | |
304 | } | |
305 | # print "LibOut: '$libout'\nLFlags: @lflags\nOfiles: @objfiles\n"; | |
306 | # exit(1); | |
307 | foreach (@objfiles) { | |
308 | my $sourcefile = $_; | |
865406bc | 309 | $sourcefile =~ s/\.o$/.c/; |
259d87c3 MSO |
310 | push(@sources, $sourcefile); |
311 | push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}}); | |
312 | push(@defines, @{$compile_options{"${sourcefile}_DEFINES"}}); | |
313 | push(@incpaths, @{$compile_options{"${sourcefile}_INCPATHS"}}); | |
314 | } | |
315 | removeDuplicates(); | |
316 | ||
317 | push(@{$build_structure{"LIBS"}}, $libout); | |
318 | @{$build_structure{"LIBS_${libout}"}} = ("_DEFINES", "_INCLUDES", "_CFLAGS", "_SOURCES", | |
319 | "_OBJECTS"); | |
320 | @{$build_structure{"LIBS_${libout}_DEFINES"}} = @defines; | |
321 | @{$build_structure{"LIBS_${libout}_INCLUDES"}} = @incpaths; | |
322 | @{$build_structure{"LIBS_${libout}_CFLAGS"}} = @cflags; | |
323 | @{$build_structure{"LIBS_${libout}_LFLAGS"}} = @lflags; | |
324 | @{$build_structure{"LIBS_${libout}_SOURCES"}} = @sources; | |
325 | @{$build_structure{"LIBS_${libout}_OBJECTS"}} = @objfiles; | |
326 | clearCompileStep(); | |
327 | } | |
328 | ||
329 | sub handleLinkLine | |
330 | { | |
331 | my ($line, $lineno) = @_; | |
332 | my (@objfiles, @lflags, @libs, $appout, $part); | |
d8c07013 | 333 | my @parts = shellwords($line); |
259d87c3 MSO |
334 | shift(@parts); # ignore cmd |
335 | while ($part = shift @parts) { | |
336 | if ($part =~ /^-IGNORE/) { | |
337 | push(@lflags, $part); | |
338 | } elsif ($part =~ /^-[GRIMDO]/) { | |
339 | # eat compiler flags | |
340 | } elsif ("$part" eq "-o") { | |
341 | $appout = shift @parts; | |
342 | } elsif ("$part" eq "-lz") { | |
343 | push(@libs, "zlib.lib"); | |
c36e1638 MSO |
344 | } elsif ("$part" eq "-lcrypto") { |
345 | push(@libs, "libeay32.lib"); | |
38743b7d | 346 | } elsif ("$part" eq "-lssl") { |
c36e1638 | 347 | push(@libs, "ssleay32.lib"); |
259d87c3 MSO |
348 | } elsif ($part =~ /^-/) { |
349 | push(@lflags, $part); | |
350 | } elsif ($part =~ /\.(a|lib)$/) { | |
351 | $part =~ s/\.a$/.lib/; | |
352 | push(@libs, $part); | |
865406bc PO |
353 | } elsif ($part eq 'invalidcontinue.obj') { |
354 | # ignore - known to MSVC | |
355 | } elsif ($part =~ /\.o$/) { | |
259d87c3 | 356 | push(@objfiles, $part); |
865406bc PO |
357 | } elsif ($part =~ /\.obj$/) { |
358 | # do nothing, 'make' should not be producing .obj, only .o files | |
259d87c3 | 359 | } else { |
90d5170c | 360 | die "Unhandled link option @ line $lineno: $part"; |
259d87c3 MSO |
361 | } |
362 | } | |
363 | # print "AppOut: '$appout'\nLFlags: @lflags\nLibs : @libs\nOfiles: @objfiles\n"; | |
364 | # exit(1); | |
365 | foreach (@objfiles) { | |
366 | my $sourcefile = $_; | |
865406bc | 367 | $sourcefile =~ s/\.o$/.c/; |
259d87c3 MSO |
368 | push(@sources, $sourcefile); |
369 | push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}}); | |
370 | push(@defines, @{$compile_options{"${sourcefile}_DEFINES"}}); | |
371 | push(@incpaths, @{$compile_options{"${sourcefile}_INCPATHS"}}); | |
372 | } | |
373 | removeDuplicates(); | |
374 | ||
375 | removeDuplicates(); | |
376 | push(@{$build_structure{"APPS"}}, $appout); | |
377 | @{$build_structure{"APPS_${appout}"}} = ("_DEFINES", "_INCLUDES", "_CFLAGS", "_LFLAGS", | |
378 | "_SOURCES", "_OBJECTS", "_LIBS"); | |
379 | @{$build_structure{"APPS_${appout}_DEFINES"}} = @defines; | |
380 | @{$build_structure{"APPS_${appout}_INCLUDES"}} = @incpaths; | |
381 | @{$build_structure{"APPS_${appout}_CFLAGS"}} = @cflags; | |
382 | @{$build_structure{"APPS_${appout}_LFLAGS"}} = @lflags; | |
383 | @{$build_structure{"APPS_${appout}_SOURCES"}} = @sources; | |
384 | @{$build_structure{"APPS_${appout}_OBJECTS"}} = @objfiles; | |
385 | @{$build_structure{"APPS_${appout}_LIBS"}} = @libs; | |
386 | clearCompileStep(); | |
387 | } |