Ignore mingw-get's downloaded files
[msysgit.git] / mingw / bin / dsw2mak
blobc84028dea1d878883f2852684a8295987b671144
1 #!gawk -f
2 # dsw2mak.awk
4 # An Awk script that generates a unix Makefile from a
5 # Microsoft Developer Studio workspace file.
7 # Copyright (C) 2001  José Fonseca
9 # This program is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public License
11 # as published by the Free Software Foundation; either version 2
12 # of the License, or (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU Lesser General Public License, http://www.gnu.org/copyleft/lesser.html
18 # for more details.
20 # José Fonseca <j_r_fonseca@yahoo.co.uk>
22 # Features:
23 #   - generation of GUI applications (including resource files), 
24 #     DLLs, console applications and static libraries
25 #   - translations of the most common compiler and linker options
26 #   - conversion of workspace files (.dsw) and all associated 
27 #     projects files (.dsp) generating all necessary Makefiles
28 #   - handling of nested !IF, !ELSEIF and !ENDIF maintaining the 
29 #     same build configurations as the original project
30 #   - automatic generation of the dependencies
32 # Example:
33 #   gawk -f dsw2mak.awk MyApp.dsw
35 # Notes:
36 #   - Make sure that both this script and the input files are in
37 #     a line ending convention that gawk version in your system
38 #     can handle.
39 #   - If an option is not handled by this script don't edit all 
40 #     generate Makefiles by hand. Add support for the option in 
41 #     this script and submit your additions to the author.
43 # Changelog (incomplete):
44 #   2003-11-25: Amitai Yuval
45 #     Strip DOS line-endings from DSW's too.
46 #  
47 #   2002-11-07: Alain Touret
48 #     Fix bug in the linker output target determination.
49 #     Support for C++ source files with .cc and .cxx extensions.
51 #   2001-02-18: José Fonseca
52 #     Improved linker libraries and options handling.
53 #     Debug output.
54 #     Better handling of custom builds
56 #   2001-02-15: José Fonseca
57 #     Improved C compiler options handling.
58 #     More verbose warning output.
60 #   2001-02-14: José Fonseca
61 #     Added comments to the source code.
65 # check and remove unnecessary quotes from a string
66 function fixquotes(str) {
67         if(str ~ /^"[^[:blank:]]+"$/) {
68                 sub(/^"/, "", str);
69                 sub(/"$/, "", str);
70         }
71         
72         return str
75 # fixes a path string
76 function fixpath(path) {
77         # remove leading and trainling whitespaces
78         sub(/^[[:blank:]]+/, "", path);
79         sub(/[[:blank:]]+$/, "", path);
80         
81         # check and remove unnecessary quotes
82         path = fixquotes(path)
83         
84         # change the forward slashes to backslashes
85         gsub(/\\/, "/", path)
86         
87         # remove reduntant ./ directories
88         gsub(/^(\.\/)+/, "", path)
89         gsub(/^\/(\.\/)+/, "", path)
90         
91         return path
94 # get the base directory from a path
95 function basedir(path) {
96         # remove leading and trainling whitespaces
97         sub(/^[[:blank:]]+/, "", path);
98         sub(/[[:blank:]]+$/, "", path);
99         
100         # remove the quotes
101         if(path ~ /^".+"$/) {
102                 sub(/^"/, "", path);
103                 sub(/"$/, "", path);
104         }
105         
106         # remove the leading path
107         sub(/(^|[\/\\:])[^\/\\:]*$/, "", path)
108         
109         # add quotes if needed
110         if(path ~ /[[:blank:]]/)
111                 path = "\"" path "\""
112         
113         return path
116 # get the filename from a path
117 function basefile(path) {
118         # remove leading and trainling whitespaces
119         sub(/^[[:blank:]]+/, "", path);
120         sub(/[[:blank:]]+$/, "", path);
121         
122         # remove the quotes
123         if(path ~ /^".+"$/) {
124                 sub(/^"/, "", path);
125                 sub(/"$/, "", path);
126         }
127         
128         # remove the trailing path
129         sub(/^.*[\/\\:]/, "", path)
130         
131         # add quotes if needed
132         if(path ~ /[[:blank:]]/)
133                 path = "\"" path "\""
134                 
135         return path
138 # skip lines until matching a given regular expression
139 # NOTE: not used but it could be eventually handy
140 function skip(regexp, infile, ret) {
141         while((ret = getline < infile) == 1 && $0 !~ regexp) {}
142         
143         return ret
146 # parses a project file (.dsp) specified by 'infile' and generates a makefile to 'outfile'
147 function parse_dsp(infile, outfile, i) {
148         print infile
149         
150         # this specifies verbose debug output
151         debug = 0
153         # this specifies a prefix to the binutils and gcc binaries
154         #prefix = "mingw32-"
155         prefix = ""
157         # this specifies the name of the 'rm -f' or equivalent command
158         rm = "rm -f"
160         # check for a bad file
161         if((getline < infile) == -1) { 
162                 print infile ": " ERRNO
163                 return
164         }
165         
166         # Strip DOS line-endings
167         gsub(/\r$/, "")
168         
169         # count the number of lines
170         inline = 1
171         
172         # print the Makefile header
173         print "# Makefile - " basefile(infile) > outfile
174         print "" > outfile
176         # this specifies the default name for the dependencies file
177         dependencies = ".dependencies"
179         # attemp to get the project name
180         if(/^# Microsoft Developer Studio Project File/) {
181                 name = gensub(/^# Microsoft Developer Studio Project File - Name="(.*)".*$/, "\\1", "1")
182                 dependencies = name ".dep"
183         }
185         # main loop
186         while((getline < infile) == 1) {
187                 # Strip DOS line-endings
188                 gsub(/\r$/, "")
189                 
190                 # increment the number of lines
191                 inline = inline + 1
192                 
193                 # catch the target type definition
194                 if(/^# TARGTYPE/) {
195                         if (/[[:space:]]0x0101$/) {
196                                 # Win32 (x86) Application
197                                 exeflag = 1
198                                 dllflag = 0
199                                 libflag = 0
200                         }
201                         if (/[[:space:]]0x0102$/) {
202                                 # Win32 (x86) Dynamic-Link Library
203                                 exeflag = 0
204                                 dllflag = 1
205                                 libflag = 0
206                         }
207                         if (/[[:space:]]0x0103$/) {
208                                 # Win32 (x86) Console Application
209                                 exeflag = 1
210                                 dllflag = 0
211                                 libflag = 0
212                         }
213                         if (/[[:space:]]0x0104$/) {
214                                 # Win32 (x86) Static Library
215                                 exeflag = 0
216                                 dllflag = 0
217                                 libflag = 1
218                         }
219                         continue
220                 }
221                 
222                 # catch the default configuration definition
223                 if(/^CFG=/) {
224                         print "ifndef CFG" > outfile
225                         print > outfile
226                         print "endif" > outfile
227                 }
228                 
229                 # deal with the preprocessor commands
230                 if(/^!/) {
231                         # as GNU make doesn't have the '!ELSEIF' equivalent we have to use nested 'if ... else .. endif' to obtain the same effect
232                         # a stack is used to keep track of the current nested level
233                 
234                         if(/^!IF/) {
235                                 $0 = gensub(/^!IF[[:space:]]+(.+)[[:space:]]*==[[:space:]]*(.+)$/, "ifeq \\1 \\2", "1")
236                                 $0 = gensub(/^!IF[[:space:]]+(.+)[[:space:]]*!=[[:space:]]*(.+)$/, "ifneq \\1 \\2", "1")
237                                 print > outfile
238                                 stacktop += 1
239                                 stack[stacktop] = 1
240                                 continue
241                         }
242                         if(/^!ELSE$/) {
243                                 print "else"
244                         }
245                         if(/^!ELSEIF/) {
246                                 $0 = gensub(/^!ELSEIF[[:space:]]+(.+)[[:space:]]*==[[:space:]]*(.+)$/, "else\nifeq \\1 \\2", "1")
247                                 $0 = gensub(/^!ELSEIF[[:space:]]+(.+)[[:space:]]*!=[[:space:]]*(.+)$/, "else\nifneq \\1 \\2", "1")
248                                 print > outfile
249                                 stack[stacktop] += 1
250                                 continue
251                         }
252                         if(/^!ENDIF[[:space:]]*$/) {
253                                 for (i = 0; i < stack[stacktop]; i++)
254                                         print "endif" > outfile
255                                 stacktop -= 1
256                                 continue
257                         }
258                 }
259                 
260                 # catch the C++ compiler definition
261                 if(/^CPP=/) {
262                         print "CC=" prefix "gcc" > outfile
263                         print "CFLAGS=" > outfile
264                         print "CXX=" prefix "g++" > outfile
265                         print "CXXFLAGS=$(CFLAGS)" > outfile
266                         
267                         continue
268                 }
269                 
270                 # catch the C++ compiler flags
271                 if(/^# ADD CPP /) {
272                         if (debug)
273                                 print infile ":" inline ": " $0
274                 
275                         # extract the flags from the line
276                         cflags = $0
277                         sub(/^# ADD CPP /, "", cflags)
278                         
279                         split(" " cflags, options, /[[:space:]]+\//)
280                         
281                         cflags = ""
282                         for(i in options) {
283                                 option = options[i]
284                         
285                                 # translate the options
286                                 # some of the translations effectively remove the option (and its arguments) since there is no translation equivalent
287                                 
288                                 if (option == "") {
289                                 } else if(option ~ /^nologo$/) {
290                                         # Suppress Startup Banner and Information Messages
291                                         option = "" 
292                                 } else if (option ~ /^W0$/) {
293                                         # Turns off all warning messages
294                                         option = "-w"
295                                 } else if (option ~ /^W[123]$/) {
296                                         # Warning Level
297                                         option = "-W"
298                                 } else if (option ~ /^W4$/) {
299                                         # Warning Level
300                                         option = "-Wall"
301                                 } else if (option ~ /^WX$/) {
302                                         # Warnings As Errors
303                                         option = "-Werror"
304                                 } else if (option ~ /^Gm$/) {
305                                         # Enable Minimal Rebuild
306                                         option = "" 
307                                 } else if (option ~ /^GX$/) {
308                                         # Enable Exception Handling
309                                         option = "-fexceptions" 
310                                 } else if (option ~ /^Z[d7iI]$/) {
311                                         # Debug Info
312                                         option = "-g" 
313                                 } else if (option ~ /^Od$/) {
314                                         # Disable Optimizations
315                                         option = "-O0" 
316                                 } else if (option ~ /^O1$/) {
317                                         # Minimize Size
318                                         option = "-Os" 
319                                 } else if (option ~ /^O2$/) {
320                                         # Maximize Speed
321                                         option = "-O2" 
322                                 } else if (option ~ /^Ob0$/) {
323                                         # Disables inline Expansion 
324                                         option = "-fno-inline"
325                                 } else if (option ~ /^Ob1$/) {
326                                         # In-line Function Expansion
327                                         option = "" 
328                                 } else if (option ~ /^Ob2$/) {
329                                         # auto In-line Function Expansion
330                                         option = "-finline-functions" 
331                                 } else if (option ~ /^Oy$/) {
332                                         # Frame-Pointer Omission
333                                         option = "-fomit-frame-pointer" 
334                                 } else if (option ~ /^GZ$/) {
335                                         # Catch Release-Build Errors in Debug Build
336                                         option = "" 
337                                 } else if (option ~ /^M[DLT]d?$/) {
338                                         # Use Multithreaded Run-Time Library
339                                         option = "" 
340                                 } else if (option ~ /^D/) {
341                                         # Preprocessor Definitions
342                                         gsub(/^D[[:space:]]*/, "", option)
343                                         option = "-D" fixquotes(option)
344                                 } else if (option ~ /^I/) {
345                                         # Additional Include Directories
346                                         gsub(/^I[[:space:]]*/, "", option)
347                                         option = "-I" fixpath(option)
348                                 } else if (option ~ /^U/) {
349                                         # Undefines a previously defined symbol
350                                         gsub(/^U[[:space:]]*/, "", option)
351                                         option = "-U" fixquotes(option)
352                                 } else if (option ~ /^Fp/) {
353                                         # Name .PCH File
354                                         option = "" 
355                                 } else if (option ~ /^F[Rr]/) {
356                                         # Create .SBR File
357                                         option = "" 
358                                 } else if (option ~ /^YX$/) {
359                                         # Automatic Use of Precompiled Headers
360                                         option = "" 
361                                 } else if (option ~ /^FD$/) {
362                                         # Generate File Dependencies
363                                         option = "" 
364                                 } else if (option ~ /^c$/) {
365                                         # Compile Without Linking
366                                         # this option is always present and is already specified in the suffix rules
367                                         option = "" 
368                                 } else if (option ~ /^GB$/) {
369                                         # Blend Optimization
370                                         option = "-mcpu=pentiumpro -D_M_IX86=500"
371                                 } else if (option ~ /^G6$/) {
372                                         # Pentium Pro Optimization
373                                         option = "-march=pentiumpro -D_M_IX86=600"
374                                 } else if (option ~ /^G5$/) {
375                                         # Pentium Optimization
376                                         option = "-mcpu=pentium -D_M_IX86=500"
377                                 } else if (option ~ /^G3$/) {
378                                         # 80386 Optimization
379                                         option = "-mcpu=i386 -D_M_IX86=300"
380                                 } else if (option ~ /^G4$/) {
381                                         # 80486 Optimization
382                                         option = "-mcpu=i486 -D_M_IX86=400"
383                                 } else if (option ~ /^Yc/) {
384                                         # Create Precompiled Header
385                                         option = ""
386                                 } else if (option ~ /^Yu/) {
387                                         # Use Precompiled Header
388                                         option = ""
389                                 } else if (option ~ /^Za$/) {
390                                         # Disable Language Extensions
391                                         option = "-ansi"
392                                 } else if (option ~ /^Ze$/) {
393                                         # Enable Microsoft Extensions
394                                         print infile ":" inline ": /" option ": Enable Microsoft Extensions option ignored" > "/dev/stderr"
395                                         option = "" 
396                                 } else if (option ~ /^Zm[[:digit:]]+$/) {
397                                         # Specify Memory Allocation Limit
398                                         option = "" 
399                                 } else if (option ~ /^Zp1$/) {
400                                         # Packs structures on 1-byte boundaries
401                                         option = "-fpack-struct" 
402                                 } else if (option ~ /^Zp(2|4|8|16)?$/) {
403                                         # Struct Member Alignment
404                                         option = "" 
405                                         print infile ":" inline ": /" option ": Struct Member Alignment option ignored" > "/dev/stderr"
406                                 } else {
407                                         print infile ":" inline ": /" option ": C compiler option not implemented" > "/dev/stderr"
408                                         option = ""
409                                 }
410                                 
411                                 if (option != "") {
412                                         if(cflags == "")
413                                                 cflags = option
414                                         else
415                                                 cflags = cflags " " option
416                                 }
417                                         
418                         }
419                         
420                         # change the slashes
421                         gsub(/\\/, "/", cflags)
422                         
423                         print "CFLAGS+=" cflags > outfile
424                         
425                         if (debug)
426                                 print outfile ": " "CFLAGS+=" cflags
427                         
428                         continue
429                 }
430                 
431                 # catch the linker definition
432                 if(/^LINK32=/) {
433                         if (exeflag)
434                                 print "LD=$(CXX) $(CXXFLAGS)" > outfile
435                         if (dllflag)
436                                 print "LD=" prefix "dllwrap" > outfile
437                         
438                         print "LDFLAGS=" > outfile
439                         
440                         continue
441                 }
442                 
443                 # catch the linker flags
444                 if(/^# ADD LINK32 /) {
445                         if (debug)
446                                 print infile ":" inline ": " $0
447                                 
448                         # extract the flags from the line
449                         ldflags = $0
450                         sub(/^# ADD LINK32 /, "", ldflags)
451                         
452                         split(ldflags, options, /[[:space:]]+\//)
453                         
454                         # attempts to get the used libraries to a seperate variable
455                         libs = options[1]
456                         libs = gensub(/([[:alnum:]/\\_-]+)\.lib/, "-l\\1", "g", libs)
457                         delete options[1]
458                         
459                         ldflags = ""
460                         for(i in options) {
461                                 option = options[i]
462                         
463                                 # translate the options
464                                 # some of the translations effectively remove the option (and its arguments) since there is no translation equivalent
465                                 if (option == "") {
466                                 } else if (option ~ /^base:/) {
467                                         # Base Address
468                                         gsub(/^base:/, "--image-base ", option)
469                                 } else if (option ~ /^debug$/) {
470                                         # Generate Debug Info
471                                         option = ""
472                                 } else if (option ~ /^dll$/) {
473                                         # Build a DLL
474                                         dllflag = 1
475                                 
476                                         # remove this option since the DLL output option is handled by the suffix rules
477                                         option = ""
478                                 } else if (option ~ /^incremental:[[:alpha:]]+$/) {
479                                         # Link Incrmentally
480                                         option = ""
481                                 } else if (option ~ /^implib:/) {
482                                         # Name import library
483                                         gsub(/^implib:/, "", option)
484                                         option = "--implib " fixpath(gensub(/([[:alnum:]_-]+)\.lib/, "lib\\1.a", "g", option))
485                                 } else if (option ~ /^libpath:/) {
486                                         # Additional Libpath
487                                         gsub(/^libpath:/, "", option)
488                                         option = "-L" fixpath(option)
489                                 } else if (option ~ /^machine:[[:alnum:]]+$/) {
490                                         # Specify Target Platform
491                                         option = ""
492                                 } else if (option ~ /^map/) {
493                                         # Generate Mapfile
494                                         if (option ~ /^map:/)
495                                                 gsub(/^map:/, "-Map ", option)
496                                         else
497                                                 option = "-Map " name ".map"
498                                 } else if(option ~ /^nologo$/) {
499                                         # Suppress Startup Banner and Information Messages
500                                         option = "" 
501                                 } else if (option ~ /^out:/) {
502                                         # Output File Name
503                                         target = fixpath(gensub(/out:("[^"]+"|[^[:space:]]+).*$/, "\\1", "1", option))
504                                         
505                                         print "TARGET=" target > outfile
506                                         
507                                         # remove this option since the output option is handled by the suffix rules
508                                         option = ""
509                                 } else if (option ~ /^pdbtype:/) {
510                                         # Program Database Storage
511                                         option = ""
512                                 } else if (option ~ /^subsystem:/) {
513                                         # Specify Subsystem
514                                         gsub(/^subsystem:/, "-Wl,--subsystem,", option) 
515                                 } else if (option ~ /^version:[[:digit:].]+$/) {
516                                         # Version Information
517                                         option = ""
518                                 } else {
519                                         print infile ":" inline ": /" option ": linker option not implemented" > "/dev/stderr"
520                                         option = ""
521                                 }
522                                 
523                                 if (option != "") {
524                                         if(ldflags == "")
525                                                 ldflags = option
526                                         else
527                                                 ldflags = ldflags " " option
528                                 }
529                                         
530                         }
531                         
532                         # attempt to get the name of the target from the '/out:' option
533                         if (ldflags ~ /\/out:/) {       # Output File Name
534                         }
535                         
536                         # change the slashes
537                         gsub(/\\/, "/", ldflags)
538                                                 
539                         print "LDFLAGS+=" ldflags > outfile
540                         print "LIBS+=" libs > outfile
541                         
542                         if (debug) {
543                                 print outfile ": " "LDFLAGS+=" ldflags
544                                 print outfile ": " "LIBS+=" libs
545                         }
546                         
547                         continue
548                 }
549                 
550                 # catch the library archiver definition
551                 if(/^LIB32=/) {
552                         libflag = 1
553                         
554                         print "AR=" prefix "ar" > outfile
555                         
556                         continue
557                 }
559                 # catch the library archiver flags
560                 if(/^# ADD LIB32 /) {
561                         # extract the flags from the line
562                         arflags = $0
563                         sub(/^# ADD LIB32 /, "", arflags)
564                         
565                         # translate the options
566                         gsub(/\/nologo[[:space:]]*/, "", arflags)               # Suppress Startup Banner and Information Messages
567                         gsub(/\/machine:[[:alnum:]]+[[:space:]]*/, "", arflags) # Specify Target Platform
568                         
569                         # attempt to get the name of the target from the '/out:' option
570                         if (arflags ~ /\/out:/) {
571                                 target = fixpath(gensub(/^.*\/out:(".*"|[^[:space:]]+).*$/, "\\1", "1", arflags))
572                                 target = basedir(target) "/lib" basefile(gensub(/(\.[^.]*)?$/, ".a", 1, target))
573                                 
574                                 print "TARGET=" target > outfile
575                                 
576                                 # remove this option since the output option is handled differentely
577                                 sub(/\/out:(".*"|[^[:space:]]+)/, "", arflags)
578                         }
579                         
580                         # change the slashes
581                         gsub(/\\/, "/", arflags)
582                         
583                         print "ARFLAGS=rus" > outfile
584                         
585                         continue
586                 }
587                 
588                 # catch the resource compiler definition
589                 if(/^RSC=/) {
590                         print "RC=" prefix "windres -O COFF" > outfile
591                         continue
592                 }
593                 
594                 # handle the begin of the target definition
595                 if(/^# Begin Target$/) {
596                         print "" > outfile
597                 
598                         # print the default target name definition
599                         print "ifndef TARGET" > outfile
600                         if(exeflag)
601                                 print "TARGET=" name ".exe" > outfile
602                         if(dllflag)
603                                 print "TARGET=" name ".dll" > outfile
604                         if(libflag)
605                                 print "TARGET=lib" name ".a" > outfile
606                         print "endif" > outfile
607                         print "" > outfile
608                 
609                         # print the default target and the suffix rules
610                         print ".PHONY: all" > outfile
611                         print "all: $(TARGET)" > outfile
612                         print "" > outfile
613                         print "%.o: %.c" > outfile
614                         print "\t$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<" > outfile
615                         print "" > outfile
616                         print "%.o: %.cc" > outfile
617                         print "\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ -c $<" > outfile
618                         print "" > outfile
619                         print "%.o: %.cpp" > outfile
620                         print "\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ -c $<" > outfile
621                         print "" > outfile
622                         print "%.o: %.cxx" > outfile
623                         print "\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ -c $<" > outfile
624                         print "" > outfile
625                         print "%.res: %.rc" > outfile
626                         print "\t$(RC) $(CPPFLAGS) -o $@ -i $<" > outfile
627                         print "" > outfile
628                 
629                         # initialize some bookeeping variables
630                         ngroups = 0     # number of groups in the target
631                         nsources = 0    # number of isolated sources in the target
632                         groupflag = 0   # state variable that indicates if we are inside or outside of a group definition
633                         
634                         continue
635                 }
636                 
637                 # handle the end of a target definition
638                 if(/^# End Target$/) {
639                         # print the sources files definition that includes...
640                         printf "SRCS=" > outfile
641                         
642                         # ... the sources groups variables...
643                         for (i = 0; i < ngroups; i++)
644                                 printf "$(%s) ", groups[i] > outfile
645                         
646                         # ... and isolated sources not included in any group
647                         if (nsources) {
648                                 print " \\" > outfile
649                                 for (i = 0; i < nsources - 1; i++)
650                                         print "\t" sources[i] " \\" > outfile
651                                 print "\t" sources[i] > outfile
652                         }
653                         else
654                                 print "" > outfile
655                         print "" > outfile
656                 
657                         # define the objects automatically from the sources in the Makefile
658                         print "OBJS=$(patsubst %.rc,%.res,$(patsubst %.cxx,%.o,$(patsubst %.cpp,%.o,$(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(filter %.c %.cc %.cpp %.cxx %.rc,$(SRCS)))))))" > outfile
659                         print "" > outfile
660                 
661                         # print the target rule, according with the type of target
662                         print "$(TARGET): $(OBJS)" > outfile
663                         if (exeflag)
664                                 print "\t$(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)" > outfile
665                         if (dllflag)
666                                 print "\t$(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)" > outfile
667                         if (libflag)
668                                 print "\t$(AR) $(ARFLAGS) $@ $(OBJS)" > outfile
669                         print "" > outfile
670                         
671                         continue
672                 }
673                 
674                 # gather groups of source files to put them in diferent variables in the Makefile
675                 if(/^# Begin Group/) {
676                         # get the group name
677                         groupname = gensub(/^# Begin Group "(.*)"$/, "\\1", "1")
678                         
679                         # take the variable name as the upper case of the group name and changing the spaces to underscores
680                         groupvarname = toupper(groupname)
681                         gsub(/[[:space:]]/, "_", groupvarname)
682                         
683                         # add this information to the groups array
684                         groups[ngroups] = groupvarname
685                         ngroups += 1
686                 
687                         # initialize some bookeeping variables
688                         ngsources = 0   # number of sources in this group
689                         
690                         # signal that we are inside a group
691                         groupflag = 1
692                         
693                         continue
694                 }
695                 if(/^# End Group$/) {
696                         # print the group source variable definition
697                         printf "%s=", groupvarname > outfile
698                         if (ngsources) {
699                                 for (i = 0; i < ngsources; i++)
700                                         printf " \\\n\t%s", gsources[i] > outfile
701                         }
702                         print "" > outfile
703                         print "" > outfile
704                         
705                         # signal that we are outside a group
706                         groupflag = 0
707                         
708                         continue
709                 }
710                 
711                 if (/^SOURCE=/) {
712                         # get the source file name
713                         source = fixpath(gensub(/^SOURCE=(.*)$/, "\\1", "1"))
714                         
715                         # add to the group sources or isolated sources according we are in a group or not
716                         if (groupflag)
717                         {
718                                 gsources[ngsources] = source
719                                 ngsources += 1
720                         }
721                         else
722                         {
723                                 sources[nsources] = source
724                                 nsources += 1
725                         }
726                         
727                         continue
728                 }
729                 
730                 # attempts to handle custom builds definition
731                 if(/^# Begin Custom Build/) {
732                         print infile ":" inline ": " source ": Custom Build" > "/dev/stderr"
733                         
734                         # signal we are inside a custom build definition
735                         customflag = 1
736                         ncustomvars = 0
737                         
738                         continue
739                 }
740                 if(/^# End Custom Build/) {
741                         # signal we are leaving a custom build definition
742                         customflag = 0
743                         
744                         continue
745                 }
746                 if(customflag) {
747                         if (debug)
748                                 print infile ": " $0
749                                 
750                         # MSDS handles customs builds defining a series of variables for the user convenience
751                         # handle their definition ...
752                         if($0 ~ /^IntDir=/) {
753                                 gsub(/^IntDir=/, "", $0)
754                                 Intdir = fixpath($0)
755                                 continue
756                         }
757                         if($0 ~ /^IntPath=/) {
758                                 gsub(/^IntPath=/, "", $0)
759                                 IntPath = fixpath($0)
760                                 continue
761                         }
762                         if($0 ~ /^OutDir=/) {
763                                 gsub(/^OutDir=/, "", $0)
764                                 OutDir_ = fixpath($0)
765                                 OutDir = "."
766                                 continue
767                         }
768                         if($0 ~ /^InputDir=/) {
769                                 gsub(/^InputDir=/, "", $0)
770                                 InputDir = fixpath($0)
771                                 continue
772                         }
773                         if($0 ~ /^InputName=/) {
774                                 gsub(/^InputName=/, "", $0)
775                                 InputName = fixquotes($0)
776                                 continue
777                         }
778                         if($0 ~ /^InputPath=/) {
779                                 gsub(/^InputPath=/, "", $0)
780                                 InputPath = fixpath($0)
781                                 continue
782                         }
783                         if($0 ~ /^TargetDir=/) {
784                                 gsub(/^TargetDir=/, "", $0)
785                                 TargetDir_ = fixpath($0)
786                                 TargetDir = "."
787                                 continue
788                         }
789                         if($0 ~ /^TargetPath=/) {
790                                 gsub(/^TargetPath=/, "", $0)
791                                 gsub(TargetDir_, ".", $0)
792                                 TargetPath = fixpath($0)
793                                 continue
794                         }
795                         
796                         # ... and substitute them in the rules
797                         gsub(/\$\(IntDir\)/, IntDir)
798                         gsub(/\$\(IntPath\)/, IntPath)
799                         gsub(/\$\(OutDir\)/, OutDir)
800                         gsub(/\$\(InputDir\)/, InputDir)
801                         gsub(/\$\(InputName\)/, InputName)
802                         gsub(/\$\(InputPath\)/, InputPath)
803                         gsub(/\$\(TargetDir\)/, TargetDir)
804                         gsub(/\$\(TargetPath\)/, TargetPath)
805                         
806                         gsub(/\$\(SOURCE\)/, source)
807                         gsub(/"\$\(INTDIR\)"[[:space:]]*/, "")
808                         gsub(/"\$\(OUTDIR\)"[[:space:]]*/, "")
809                         
810                         # do a serie of generic actions to convert the rule
811                         gsub(/^   /, "\t")
812                         gsub(/\\/, "/")
813                         gsub(/\/$/, "\\")
814                         gsub(/\.obj/, ".o")
815                         
816                         print > outfile
817                         
818                         if (debug)
819                                 print outfile ": " $0
820                 }
821         }
823         # print the 'clean' target rule
824         print ".PHONY: clean" > outfile
825         print "clean:" > outfile
826         print "\t-" rm " $(OBJS) $(TARGET) " dependencies > outfile
827         print "" > outfile
829         # print the 'depends' target rule for automatic dependencies generation
830         print ".PHONY: depends" > outfile
831         print "depends:" > outfile
832         print "\t-$(CXX) $(CXXFLAGS) $(CPPFLAGS) -MM $(filter %.c %.cc %.cpp %.cxx,$(SRCS)) > " dependencies> outfile
833         print "" > outfile
834         print "-include " dependencies > outfile
835         print "" > outfile
836         
837         # close the files
838         close(outfile)
839         close(infile)
842 # parses a workpace file (.dsw) specified by 'infile' and generates a makefile to 'outfile'
843 function parse_dsw(infile, outfile, i)
845         print infile
846         
847         # print the Makefile header
848         print "# Makefile - " basefile(infile) > outfile
849         print "" > outfile
851         # initialize the number of projects counter
852         nprojects = 0
853         
854         # main loop
855         while((getline < infile) == 1) {
856                 # Strip DOS line-endings
857                 gsub(/\r$/, "")
858         
859                 # catch a project definition
860                 if(/^Project:/) {
861                         # increment the project counter
862                         project = nprojects
863                         nprojects++
865                         # extract the project name and filename
866                         project_name[project] = fixpath(gensub(/^Project:[[:blank:]]+(.*)=(.*)[[:blank:]]+-[[:blank:]]+.*$/, "\\1", 1))
867                         project_file[project] = fixpath(gensub(/^Project:[[:blank:]]+(.*)=(.*)[[:blank:]]+-[[:blank:]]+.*$/, "\\2", 1))
868                         
869                         # check for a .dsp file extension
870                         if(project_file[project] ~ /\.[Dd][Ss][Pp]$/) {
871                                 # create the output filename by renaming the file extension from .dsp to .mak
872                                 project_makefile[project] = project_file[project]
873                                 sub(/(\.[^.]*)?$/, ".mak", project_makefile[project])
874                         }
875                         else
876                                 project_makefile[project] = ""
877                 
878                         # initialize the project dependencies
879                         project_dependencies[project] = ""
881                         continue
882                 }
883                 
884                 # catch a project dependency marker
885                 if(project && /^{{{$/) {
886                         # read dependencies until the end marker
887                         while((getline < infile) == 1 && !/^}}}$/)
888                                 if(/^[[:blank:]]*Project_Dep_Name[[:blank:]]+/)
889                                         project_dependencies[project] = project_dependencies[project] " " fixpath(gensub(/^[[:blank:]]*Project_Dep_Name[[:blank:]]+(.*)$/, "\\1", 1))
890                                         
891                         continue
892                 }
893                 
894                 # catch other (perhaps important) section definitions and produce a warning
895                 if(/^[[:alpha:]]+:/)
896                 {
897                         project = 0
898                         print infile ": " gensub(/^([[:alpha:]]+):/, "\\1", 1) ": unknown section" > "/dev/stderr"
899                 }
900         }
902         # print the default target rule
903         print ".PHONY: all" > outfile
904         printf "all:" > outfile
905         for(i = 0; i < nprojects; i++)
906                 printf " \\\n\t%s", project_name[i] > outfile
907         print "" > outfile
908         print "" > outfile              
910         # print the rules for each project target
911         for(i = 0; i < nprojects; i++) {
912                 print ".PHONY: " project_name[i] > outfile
913                 print project_name[i] ":" project_dependencies[i] > outfile
914                 if(project_makefile[i] != "") {
915                         if(basedir(project_makefile[i]) == "")
916                                 print "\t$(MAKE) -f " project_makefile[i] > outfile
917                         else
918                                 print "\t$(MAKE) -C " basedir(project_makefile[i]) " -f " basefile(project_makefile[i]) > outfile
919                 }
920                 print "" > outfile
921         }
923         # print the 'clean' target rule
924         print ".PHONY: clean" > outfile
925         print "clean:" > outfile
926         for(i = 0; i < nprojects; i++)
927                 if(project_makefile[i] != "") {
928                         if(basedir(project_makefile[i]) == "")
929                                 print "\t$(MAKE) -f " project_makefile[i] " clean" > outfile
930                         else
931                                 print "\t$(MAKE) -C " basedir(project_makefile[i]) " -f " basefile(project_makefile[i]) " clean" > outfile
932                 }
933         print "" > outfile
935         # print the 'depends' target rule for automatic dependencies generation
936         print ".PHONY: depends" > outfile
937         print "depends:" > outfile
938         for(i = 0; i < nprojects; i++)
939                 if(project_makefile[i] != "") {
940                         if(basedir(project_makefile[i]) == "")
941                                 print "\t$(MAKE) -f " project_makefile[i] " depends" > outfile
942                         else
943                                 print "\t$(MAKE) -C " basedir(project_makefile[i]) " -f " basefile(project_makefile[i]) " depends" > outfile
944                 }
945         print "" > outfile
947         close(outfile)
948         close(infile)
950         # parse every project file
951         for(i = 0; i < nprojects; i++)
952                 if(project_makefile[i] != "") {
953                         if(basedir(infile) == "")
954                                 parse_dsp(project_file[i], project_makefile[i])
955                         else
956                                 parse_dsp(basedir(infile) "\\" project_file[i], basedir(infile) "\\" project_makefile[i])
957                 }
960 # main program
961 BEGIN {
962         print "dsw2mak.awk   Generates a Makefile from a .DSW/.DSP file   Jose Fonseca"
963         print ""
964         
965         # for each argument ...
966         for (i = 1; i < ARGC; i++) {
967                 infile = ARGV[i]
968                 
969                 # determine whether is a workspace or a project file and parse it
970                 if(infile ~ /\.[Dd][Ss][Ww]$/) {
971                         # create the output filename by renaming the filename to Makefile
972                         outfile = infile
973                         sub(/[^\/\\:]+$/, "Makefile", outfile)
974                         
975                         parse_dsw(infile, outfile)
976                 } else if(infile ~ /\.[Dd][Ss][Pp]$/) {
977                         # create the output filename by renaming the file extension from .dsp to .mak
978                         outfile = infile
979                         sub(/(\.[^.]*)?$/, ".mak", outfile)
980                         
981                         parse_dsp(infile, outfile)
982                 } else {
983                         print infile ": unknown file format" > "/dev/stderr"
984                 }
985         }