e7b0a5e03c594e5f8bcd497a423d1e9b8dd66d9b
[keystone-rtos/ibl.git] / src / make / makedep / makedep.gawk
1 #*****************************************************************************
2 #* FILE PURPOSE:Scan C files for include references
3 #*
4 #******************************************************************************
5 #* FILE NAME:   makedep.awk
6 #*
7 #* DESCRIPTION:
8 #*
9 #* NOTE:        Ported to gawk in 2010
10 #*
11 #* NOTE:        Processes #includes even in false conditional compilation
12 #*
13 #* (C) Copyright 1999-2001, Telogy Networks, Inc.
14 #* (C) Copyright 2010, Texas Instruments Incorperated
15 #******************************************************************************
17 BEGIN \
18 {
19 progname              = "makedep"
20 version_string        = "V1.80"
21 version_date          = "Sept 8, 2010"
23 #*******************************************************************************
24 # Types & Constants
25 #*******************************************************************************
26 true = 1
27 false = 0
29 c_re_include          =  "^[ \t]*#[ \t]*include[ \t]+"
30 s_re_include          =  "^[ \t]*\\.(include|copy)[ \t]+"
31 re_include            = c_re_include
33 #*******************************************************************************
34 # Command Line Options
35 #*******************************************************************************
36 quiet                 = false
37 no_warn               = false
38 bracket_includes      = true
39 quote_includes        = true
40 abs_paths             = true
41 recurse               = true
42 depend_not_found      = false
43 stdout_format         = false
44 include_full_report   = true
45 indent_string         = "    "
46 objext                = "obj"
47 outfile_spec          = ""
49 outfile               = ""
50 infile                = ""
51 infile_basename       = ""
52 cur_filename          = ""
54 err_file = ""
56 orig_argc = 0
59 # paths[]
60 # depends[]             # actually [][]
61 # processed_files[]
63 #*******************************************************************************
64 # Syntax
65 #*******************************************************************************
66 comment_start         = "# "
67 comment_stop          = ""
68 eol_continue          = "\\"
70 #*******************************************************************************
71 # File Processing
72 #*******************************************************************************
73 file_error            = false
75 #*******************************************************************************
76 # Program Debug & Control
77 #*******************************************************************************
78 trace_file            = "trace.out"
79 # trace_classes[]
80 exit_code             = -1
81 banner_done           = false
82 any_traces            = false
84 }
86 #*******************************************************************************
87 # Top Level Functions
88 #*******************************************************************************
89 BEGIN \
90 {
91     i = 0
92     j = 0
93     option = ""
94     arg = ""
95     sub_option = ""
96     input_found = false
97     any_gen = false
99     trace_classes[ "includes" ] = 0
100     trace_classes[ "flow" ]     = 0
101     trace_classes[ "file" ]     = 0
102     trace_classes[ "fileops" ]  = 0     # 1 = inhibit file delete rename, etc
105     paths[0] = 1
106     paths[1] = ""       # place holder for "current place" directory
108     trace( "flow", "BEGIN" )
110     err_file = "/dev/stdout"
112     for ( i=1; i < ARGC; i++)
113     {
114         if (ARGV[i] ~ /^-/)
115         {
116             option = substr(ARGV[i], 2, 1)
117             arg    = substr(ARGV[i], 3)
119             if (option == "I")
120             {
121                 if (arg == "")
122                 {
123                     # don't process this argument as a file
124                     ARGV[i] = ""
126                     i++
127                     arg = ARGV[i]
128                 }
129                 paths[0]++
130                 paths[paths[0]] = arg
131             }
132             else if (option == "a")
133             {
134                 re_include = s_re_include
135             }
136             else if (option == "e")
137             {
138                 if (arg == "")
139                 {
140                     # don't process this argument as a file
141                     ARGV[i] = ""
143                     i++
144                     arg = ARGV[i]
145                 }
146                 objext = arg    
147             }
148             else if (option == "f")
149             {
150                 depend_not_found = true
151             }
152             else if (option == "p")
153             {
154                 stdout_format = true
155             }
156             else if (option == "o")
157             {
158                 if (arg == "")
159                 {
160                     # don't process this argument as a file
161                     ARGV[i] = ""
163                     i++
164                     arg = ARGV[i]
165                 }
166                 outfile_spec = arg      
167             }
168             else if (option == "r")
169             {
170                 recurse = false
171             }
172             else if (option == "s")
173             {
174                 bracket_includes = false
175             }
176             else if (option == "q")
177             {
178                 quiet = true
179             }
180             else if (option == "w")
181             {
182                 no_warn = true
183             }
184             else if (option == "d")
185             {
186                 process_trace_option(arg)
187             }
188             else if (option == "h" || option == "?")
189             {
190                 help()
191             }
192             else
193             {
194                 fatal_error("bad option:" ARGV[i])
195             }
197             # don't process this argument as a file
198             ARGV[i] = ""
199         }
200         else
201         {
202             input_found = true
203             ARGV[i] = to_abs_path(ARGV[i])
204         }
205     }
207     if (!quiet)
208     {
209         banner()
210     }
212     if (stdout_format)
213     {
214         if (outfile_spec != "")
215         {
216             fatal_error("can't specify both -p and -o")
217         }
219         err_file = "/dev/stderr"
220         outfile  = "/dev/stdout"
221     }
223     if (outfile_spec == "")
224     {
225         outfile_spec = "./$.d"
226     }
228     if (!input_found)
229     {
230         fatal_error("nothing to do, use " progname "-h for help");
231     }
233     # we add more arguments as we go so remember the number of original args
234     orig_argc = ARGC
237 FILENAME != cur_filename \
239     cur_filename = FILENAME
240     depends[FILENAME] = ""
241     trace("flow", "Processing file: " FILENAME )
244 $0 ~ re_include \
246     num_depends = 0
247     filename = ""
248     abs_filename = ""
249     this_dir = ""
251     trace("includes", "#include line:" $0)
253     # find directory of current file
254     this_dir = file_path(FILENAME)
255     if (this_dir == "")
256     {
257         this_dir = "./"
258     }
260     # assume that the file name is the second arg
261     filename = $2
263     # is this a bracket include?
264     if (filename ~ /^</)
265     {
266         # yes, should we process it?
267         if (!bracket_includes)
268         {
269             # no
270             next
271         }
272         paths[1] = ""
273     }
274     else
275     {
276         # no, must be a quote include, should we process it?
277         if (!quote_includes)
278         {
279             # no
280             next
281         }
282         paths[1] = this_dir
283     }
285     # strip off the quotes or brackets
286     if (filename ~ /^[<"]/)
287     {
288        filename = substr(filename, 2, length(filename)-2)
289     }
291     abs_filename = find_file(filename, paths);
292     if (abs_filename == "")
293     {
294         warn("include file not found", filename)
295         abs_filename = filename
296     }
297     else
298     {
299         if (recurse && !(abs_filename in processed_files))
300         {
301             trace("flow", "queueing file " abs_filename)
302             ARGV[ARGC] = abs_filename
303             processed_files[abs_filename] = 1
304             ARGC++
305             depends_count[abs_filename] = 0
306         }
307     }
309     cur_depends                     = depends[FILENAME]
310     if (cur_depends == "")
311     {
312         depends[FILENAME]           = abs_filename
313     }
314     else
315     {
316         depends[FILENAME]           = cur_depends "|" abs_filename
317     }
319     next
322 # all other lines do nothing
324     next
327 END \
329     arg = ""
331     trace( "flow", "END" )
333     if (exit_code >= 0)
334     {
335         exit(exit_code)
336     }
338     for (arg=1; arg < orig_argc; arg++)
339     {
340         infile = ARGV[arg]
341         
342         if (infile == "")
343             continue
345         if (!stdout_format)
346         {
347             file_done()
349             infile_basename = basename(infile)
350             infile_basename = strip_ext(infile_basename)
351             outfile = outfile_spec
352             gsub("\\$", infile_basename, outfile)
354             gen_header()
355             file_error = false
356         }
358         delete processed_files
359         process_depends(infile, 1)
361         if (include_full_report)
362         {
363             gen_header(true)
364             delete processed_files
365             process_depends(infile, 1, true)
366         }
367     }
370 #*******************************************************************************
371 # Command Line Functions
372 #*******************************************************************************
373 function process_trace_option(arg,  i, setting)
375     # local i
376     # local setting
378     if ( arg ~ /^\+/ )
379     {
380         setting = 1
381     }
382     else if ( arg ~ /^-/ )
383     {
384         setting = 0
385     }
386     else
387     {
388         fatal_error("bad debug option:" ARGV[i])
389     }
391     arg = substr(arg,2)
392     if (arg == "")
393     {
394         for (i in trace_classes)
395         {
396             trace_classes[i] = setting
397         }
398     }
399     else if (arg in trace_classes)
400     {
401         trace_classes[arg] = setting
402     }
403     else
404     {
405         banner()
406         print "Valid Trace Classes are:"
407         for (i in trace_classes)
408         {
409             print "\t" i
410         }
411         fatal_error("unknown trace class " arg)
412     }
414     any_traces = false
415     for (i in trace_classes)
416     {
417         any_traces = any_traces || trace_classes[i]
418     }
421 function banner()
423     if (banner_done)
424     {
425         return
426     }
428     banner_done = true
429     print toupper(progname) " " version_string ": C include dependency generator/lister" > err_file
430     print "Last updated " version_date > err_file
433 function help()
435     banner()
436     print "usage " progname " <options> <file>"
437     print "where options is 0 or more of the following:"
438     print "(default values are listed in paren's)"
439     print "    -Idir                add an include path"
440     print "    -a                   use asm format includes"
441     print "    -eext                set obj extension to ext"
442     print "    -s                   don't process standard include files ie. <>"
443     print "    -r                   don't recurse to find dependencies of found include file"
444     print "    -o                   specify output file (./$.d)"
445     print "    -p                   pipe output to stdout w/o extra formating"
446     print "    -q                   quiet mode (no banner)"
447     print "    -w                   suppress warnings"
448     print "    -d(+|-)[trace class] this program's diagnostics (see source)"
449     print "    -h or -?             this help"
450     exit_code = 10
451     exit
454 #*******************************************************************************
455 # File Functions
456 #*******************************************************************************
457 function file_done()
459     close( outfile )
460     if ( file_error )
461     {
462         delete_file( outfile )
463     }
466 function file_time( name )
468     return ctime(filetime(name))
471 function file_exists(file,        dummy, ret)
473     ret=0;
474     if ( (getline dummy < file) >=0 )
475     {
476         # file exists (possibly empty) and can be read
477         ret = 1;
478         close(file);
479     }
480     return ret;
483 function rename_file( old_name, new_name )
485     trace( "file", "rename " old_name " to " new_name )
486     if ( !traceif( "fileops" ) )
487     {
488         delete_file( new_name )
489         if ( !file_exists( old_name ) )
490         {
491             print "File does not exist: " old_name
492         }
493         else
494         {
495             rename(old_name,new_name)
496         }
497     }
500 function delete_file( name )
502     trace( "file", "delete " name )
503     if ( !traceif( "fileops" ) && file_exists( name ) )
504     {
505         rmfile(name)
506     }
509 # return filename without extension
510 function strip_ext( filename,  new_name )
512     # local new_name
514     # match the extension ( last dot and file component after it )
515     if ( match( filename, /\.[^ \t/\\.]*$/ ) != 0 )
516     {
517         # has an extension, return everything up to but not including it
518         new_name = substr( filename, 1, RSTART-1 )
519     }
520     else
521     {
522         # does not have an extension, return it all
523         new_name = filename
524     }
526     trace( "file", "strip_ext of " filename " gives " new_name )
527     return new_name
530 # return extension
531 function file_ext( filename )
533     local new_name
535     # match the extension ( last dot and file component after it )
536     if ( match( filename, /\.[^ \t/\\.]*$/ ) != 0 )
537     {
538         # has an extension, return it
539         new_name = substr( filename, RSTART )
540     }
541     else
542     {
543         # does not have an extension, return it all
544         new_name = ""
545     }
547     trace( "file", "filename_ext of " filename " gives " new_name )
548     return new_name
551 # return filename.ext w/o leading paths
552 function basename( filename,  new_name )
554     # local new_name
555     
556     new_name = filename
558     # turn all backslashes to forward slashes
559     gsub("\\", "/", new_name)
561     # match the basename (everything after the last slash)
562     if ( match( new_name, /\/[^/]*$/ ) != 0 )
563     {
564         # match found, return it
565         new_name = substr(new_name, RSTART+1)
566     }
567     else
568     {
569         # no slashes found, strip off any drive spec
570         # for this purpose a drive spec is everything before the first colon
571         if ( match( new_name, /:[^:]*$/ ) != 0 )
572         {
573             # match found, return it
574             new_name = substr(new_name, RSTART+1)
575         }
576     }
578     trace( "file", "basename of " filename " gives " new_name )
579     return new_name
582 # return drive & directories of filename
583 function file_path( filename,  new_name )
585     # local new_name
586     
587     new_name = filename
589     # turn all backslashes to forward slashes
590     gsub("\\", "/", new_name)
592     # match the basename (everything after the last slash)
593     if ( match( new_name, /\/[^/]*$/ ) != 0 )
594     {
595         # match found, return it
596         new_name = substr(new_name, 1, RSTART-1)
597     }
598     else
599     {
600         # no slashes found, strip off any drive spec
601         # for this purpose a drive spec is everything before the first colon
602         if ( match( new_name, /:[^:]*$/ ) != 0 )
603         {
604             # match found, return it
605             new_name = substr(new_name, 1, RSTART-1)
606         }
607         else
608         {
609             # no drive & no directories
610             new_name = ""
611         }
612     }
614     trace( "file", "file_path of " filename " gives " new_name )
615     return new_name
618 function to_abs_path(filename)
620     # not implemented yet
621     return filename
624 function find_file(filename, path_array,  i, test_file, num_paths)
626     # local i
627     # local test_file
628     # local num_paths
630     num_paths = path_array[0]
632     for (i=1; i <= num_paths; i++)
633     {
634         test_file = path_array[i]
636         if (test_file != "")
637         {
638             if (!(test_file ~ /\/$/))
639             {
640                 test_file = test_file "/"
641             }
642             test_file = test_file filename
643             test_file = to_abs_path(test_file)
645             if (file_exists(test_file))
646             {
647                 return test_file
648             }
649         }
650     }
652     return ""
655 #*******************************************************************************
656 # Output Processing
657 #*******************************************************************************
658 function process_depends(filename, level,    report, i, j, abs_file, num_depends, these_depends, this_indent)
660     # local i
661     # local j
662     # local abs_file
663     # local num_depends
664     # local this_indent
666     split(depends[filename], these_depends, "|")
668     # length(array) does not work on some older versions of awk/gawk
669     # also awk says that array enumeration can be in any random order
670     num_depends = 0 
671     for (i in these_depends) num_depends++  
673     if (report) 
674     {
675         this_indent = "#   "
676     }
677     else
678     {
679         this_indent = "    "
680     }
682     for (j=1; j < level; j++)
683     {
684         this_indent = this_indent indent_string
685     }
687     #trace("flow", "process_depends level=" level " : " filename " => (" num_depends ") " depends[filename] )
689     for (i=1; i <= num_depends; i++)
690     {
691         abs_file = these_depends[i]
693         trace("flow", this_indent "process_depends level=" level " report=" report " : " filename ".depends[" i "] => " these_depends[i] )
695         if (abs_file in processed_files)
696         {
697             if (report)
698             {
699                 printf("%s %-68s already included\n", \
700                     this_indent, abs_file) >outfile
701             }
702         }
703         else
704         {
705             processed_files[abs_file] = 1
707             if (abs_file in depends)
708             {
709                 if (report)
710                 {
711                     printf("%s %-68s %s\n", \
712                         this_indent, abs_file, file_time(abs_file)) >outfile
713                 }
714                 else
715                 {
716                     printf("%s %-68s %s\n", this_indent, abs_file, eol_continue) >outfile
717                 }
719                 process_depends(abs_file, level+1, report)
720             }
721             else
722             {
723                 if (report)
724                 {
725                     printf("%s %-68s not found\n", \
726                             this_indent, abs_file) >outfile
727                 }
728                 else
729                 {
730                     if (depend_not_found)
731                     {
732                         printf("%s %-68s %s\n", this_indent, abs_file, eol_continue) >outfile
733                     }
734                 }
735             }
736         }
737     }
739     # ensure line continuation is terminated
740     if (level == 1)
741         print "" >outfile
744 function gen_header( report,    infile_ext, outfile_ext)
746     # print header
748     # local   infile_ext
749     # local   outfile_ext
751     infile_ext      = file_ext(infile)
752     outfile_ext     = file_ext(outfile)
754     if (report)
755     {
756         print comment_start "Full include report for " infile comment_stop >outfile
757     }
758     else
759     {
760         print comment_start "Dependency file for " infile comment_stop >outfile
761         print comment_start "Generated on " ctime() comment_stop >outfile 
762         print comment_start "From " infile " dated " ctime(filetime(infile)) comment_stop >outfile
763         print comment_start "This file was generated by " progname ", do not edit!!" comment_stop >outfile
764         print "" >outfile
765         print infile_basename "." objext, ":", infile, infile_basename outfile_ext, eol_continue >outfile
766     }
769 #*******************************************************************************
770 # Support Functions
771 #*******************************************************************************
772 function error( desc )
774     print "Error: " desc " in " FILENAME " line " FNR >err_file
775     file_error = true
778 function ctime(time)
780     if (time == 0)
781     {
782         time = systime()
783     }
784     return strftime("%a %b %d %H:%M:%S %Z %Y", systime())
787 function filetime(filename)
789     # not implemented
790     return 1
793 function warn( desc, arg,  err )
795     # local err
797     if (no_warn)
798     {
799         return
800     }
802     if (arg != "")
803     {
804         arg = sprintf("for item %s ", arg)
805     }
807     printf("Warning: %s%s in %s line %d\n", arg, desc, FILENAME, FNR) >err_file
810 function fatal_error( desc )
812     print "Error: " desc > "/dev/stderr"
814     exit_code = 3
815     exit
818 function trace( class, msg )
820     if ( traceif( class ) )
821     {
822         print msg >trace_file
823     }
826 function traceif( class )
828     if ( !(class in trace_classes) )
829     {
830         print "trace class", class, "missing" >trace_file
831         return true
832     }
833     return ( trace_classes[class] != 0 )