Remove assert in get_def_bb_for_const
[official-gcc.git] / contrib / header-tools / reduce-headers
blobe4f4d7b123dccaf9669e555870a6af89ddf85707
1 #! /usr/bin/python2
2 import os.path
3 import sys
4 import shlex
5 import re
6 import tempfile
7 import copy
9 from headerutils import *
11 requires = { }
12 provides = { }
14 no_remove = [ "system.h", "coretypes.h", "config.h" , "bconfig.h", "backend.h" ]
16 # These targets are the ones which provide "coverage".  Typically, if any
17 # target is going to fail compilation, it's one of these.  This was determined
18 # during the initial runs of reduce-headers... On a full set of target builds,
19 # every failure which occured was triggered by one of these.  
20 # This list is used during target-list construction simply to put any of these
21 # *first* in the candidate list, increasing the probability that a failure is 
22 # found quickly.
23 target_priority = [
24     "aarch64-linux-gnu",
25     "arm-netbsdelf",
26     "avr-rtems",
27     "c6x-elf",
28     "epiphany-elf",
29     "hppa2.0-hpux10.1",
30     "i686-mingw32crt",
31     "i686-pc-msdosdjgpp",
32     "mipsel-elf",
33     "powerpc-eabisimaltivec",
34     "rs6000-ibm-aix5.1.0",
35     "sh-superh-elf",
36     "sparc64-elf",
37     "spu-elf"
41 target_dir = ""
42 build_dir = ""
43 ignore_list = list()
44 target_builds = list()
46 target_dict = { }
47 header_dict = { }
48 search_path = [ ".", "../include", "../libcpp/include" ]
50 remove_count = { }
53 # Given a header name, normalize it.  ie.  cp/cp-tree.h could be in gcc, while
54 # the same header could be referenced from within the cp subdirectory as
55 # just cp-tree.h
56 # for now, just assume basenames are unique
58 def normalize_header (header):
59   return os.path.basename (header)
62 # Adds a header file and its sub-includes to the global dictionary if they
63 # aren't already there.  Specify s_path since different build directories may
64 # append themselves on demand to the global list.
65 # return entry for the specified header, knowing all sub entries are completed
67 def get_header_info (header, s_path):
68   global header_dict
69   global empty_iinfo
70   process_list = list ()
71   location = ""
72   bname = ""
73   bname_iinfo = empty_iinfo
74   for path in s_path:
75     if os.path.exists (path + "/" + header):
76       location = path + "/" + header
77       break
79   if location:
80     bname = normalize_header (location)
81     if header_dict.get (bname):
82       bname_iinfo = header_dict[bname]
83       loc2 = ii_path (bname_iinfo)+ "/" + bname
84       if loc2[:2] == "./":
85         loc2 = loc2[2:]
86       if location[:2] == "./":
87         location = location[2:]
88       if loc2 != location:
89         # Don't use the cache if it isnt the right one.
90         bname_iinfo = process_ii_macro (location)
91       return bname_iinfo
93     bname_iinfo = process_ii_macro (location)
94     header_dict[bname] = bname_iinfo
95     # now decend into the include tree
96     for i in ii_include_list (bname_iinfo):
97       get_header_info (i, s_path)
98   else:
99     # if the file isnt in the source directories, look in the build and target
100     # directories. If it is here, then aggregate all the versions.
101     location = build_dir + "/gcc/" + header
102     build_inc = target_inc = False
103     if os.path.exists (location):
104       build_inc = True
105     for x in target_dict:
106       location = target_dict[x] + "/gcc/" + header
107       if os.path.exists (location):
108         target_inc = True
109         break
111     if (build_inc or target_inc):
112       bname = normalize_header(header)
113       defines = set()
114       consumes = set()
115       incl = set()
116       if build_inc:
117         iinfo = process_ii_macro (build_dir + "/gcc/" + header)
118         defines = set (ii_macro_define (iinfo))
119         consumes = set (ii_macro_consume (iinfo))
120         incl = set (ii_include_list (iinfo))
122       if (target_inc):
123         for x in target_dict:
124           location = target_dict[x] + "/gcc/" + header
125           if os.path.exists (location):
126             iinfo = process_ii_macro (location)
127             defines.update (ii_macro_define (iinfo))
128             consumes.update (ii_macro_consume (iinfo))
129             incl.update (ii_include_list (iinfo))
131       bname_iinfo = (header, "build", list(incl), list(), list(consumes), list(defines), list(), list())
133       header_dict[bname] = bname_iinfo
134       for i in incl:
135         get_header_info (i, s_path)
137   return bname_iinfo
140 # return a list of all headers brought in by this header
141 def all_headers (fname):
142   global header_dict
143   headers_stack = list()
144   headers_list = list()
145   if header_dict.get (fname) == None:
146     return list ()
147   for y in ii_include_list (header_dict[fname]):
148     headers_stack.append (y)
150   while headers_stack:
151     h = headers_stack.pop ()
152     hn = normalize_header (h)
153     if hn not in headers_list:
154       headers_list.append (hn)
155       if header_dict.get(hn):
156         for y in ii_include_list (header_dict[hn]):
157           if normalize_header (y) not in headers_list:
158             headers_stack.append (y)
160   return headers_list
165 # Search bld_dir for all target tuples, confirm that they have a build path with
166 # bld_dir/target-tuple/gcc, and build a dictionary of build paths indexed by
167 # target tuple..
169 def build_target_dict (bld_dir, just_these):
170   global target_dict
171   target_doct = { }
172   error = False
173   if os.path.exists (bld_dir):
174     if just_these:
175       ls = just_these
176     else:
177       ls = os.listdir(bld_dir)
178     for t in ls:
179       if t.find("-") != -1:
180         target = t.strip()
181         tpath = bld_dir + "/" + target
182         if not os.path.exists (tpath + "/gcc"):
183           print "Error: gcc build directory for target " + t + " Does not exist: " + tpath + "/gcc"
184           error = True
185         else:
186           target_dict[target] = tpath
188   if error:
189     target_dict = { }
191 def get_obj_name (src_file):
192   if src_file[-2:] == ".c":
193     return src_file.replace (".c", ".o")
194   elif src_file[-3:] == ".cc":
195     return src_file.replace (".cc", ".o")
196   return ""
198 def target_obj_exists (target, obj_name):
199   global target_dict
200   # look in a subdir if src has a subdir, then check gcc base directory.
201   if target_dict.get(target):
202     obj = target_dict[target] + "/gcc/" + obj_name
203     if not os.path.exists (obj):
204       obj = target_dict[target] + "/gcc/" + os.path.basename(obj_name)
205     if os.path.exists (obj):
206       return True
207   return False
209 # Given a src file, return a list of targets which may build this file.
210 def find_targets (src_file):
211   global target_dict
212   targ_list = list()
213   obj_name = get_obj_name (src_file)
214   if not obj_name:
215     print "Error: " + src_file + " - Cannot determine object name."
216     return list()
218   # Put the high priority targets which tend to trigger failures first
219   for target in target_priority:
220     if target_obj_exists (target, obj_name):
221       targ_list.append ((target, target_dict[target]))
223   for target in target_dict:
224     if target not in target_priority and target_obj_exists (target, obj_name):
225       targ_list.append ((target, target_dict[target]))
226         
227   return targ_list
230 def try_to_remove (src_file, h_list, verbose):
231   global target_dict
232   global header_dict
233   global build_dir
235   # build from scratch each time
236   header_dict = { }
237   summary = ""
238   rmcount = 0
240   because = { }
241   src_info = process_ii_macro_src (src_file)
242   src_data = ii_src (src_info)
243   if src_data:
244     inclist = ii_include_list_non_cond (src_info)
245     # work is done if there are no includes to check
246     if not inclist:
247       return src_file + ": No include files to attempt to remove"
249     # work on the include list in reverse.
250     inclist.reverse()
252     # Get the target list 
253     targ_list = list()
254     targ_list = find_targets (src_file)
256     spath = search_path
257     if os.path.dirname (src_file):
258       spath.append (os.path.dirname (src_file))
260     hostbuild = True
261     if src_file.find("config/") != -1:
262       # config files dont usually build on the host
263       hostbuild = False
264       obn = get_obj_name (os.path.basename (src_file))
265       if obn and os.path.exists (build_dir + "/gcc/" + obn):
266         hostbuild = True
267       if not target_dict:
268         summary = src_file + ": Target builds are required for config files.  None found."
269         print summary
270         return summary
271       if not targ_list:
272         summary =src_file + ": Cannot find any targets which build this file."
273         print summary
274         return summary
276     if hostbuild:
277       # confirm it actually builds before we do anything
278       print "Confirming source file builds"
279       res = get_make_output (build_dir + "/gcc", "all")
280       if res[0] != 0:
281         message = "Error: " + src_file + " does not build currently."
282         summary = src_file + " does not build on host."
283         print message
284         print res[1]
285         if verbose:
286           verbose.write (message + "\n")
287           verbose.write (res[1]+ "\n")
288         return summary
290     src_requires = set (ii_macro_consume (src_info))
291     for macro in src_requires:
292       because[macro] = src_file
293     header_seen = list ()
295     os.rename (src_file, src_file + ".bak")
296     src_orig = copy.deepcopy (src_data)
297     src_tmp = copy.deepcopy (src_data)
299     try:
300       # process the includes from bottom to top.  This is because we know that
301       # later includes have are known to be needed, so any dependency from this 
302       # header is a true dependency
303       for inc_file in inclist:
304         inc_file_norm = normalize_header (inc_file)
305         
306         if inc_file in no_remove:
307           continue
308         if len (h_list) != 0 and inc_file_norm not in h_list:
309           continue
310         if inc_file_norm[0:3] == "gt-":
311           continue
312         if inc_file_norm[0:6] == "gtype-":
313           continue
314         if inc_file_norm.replace(".h",".c") == os.path.basename(src_file):
315           continue
316              
317         lookfor = ii_src_line(src_info)[inc_file]
318         src_tmp.remove (lookfor)
319         message = "Trying " + src_file + " without " + inc_file
320         print message
321         if verbose:
322           verbose.write (message + "\n")
323         out = open(src_file, "w")
324         for line in src_tmp:
325           out.write (line)
326         out.close()
327           
328         keep = False
329         if hostbuild:
330           res = get_make_output (build_dir + "/gcc", "all")
331         else:
332           res = (0, "")
334         rc = res[0]
335         message = "Passed Host build"
336         if (rc != 0):
337           # host build failed
338           message  = "Compilation failed:\n";
339           keep = True
340         else:
341           if targ_list:
342             objfile = get_obj_name (src_file)
343             t1 = targ_list[0]
344             if objfile and os.path.exists(t1[1] +"/gcc/"+objfile):
345               res = get_make_output_parallel (targ_list, objfile, 0)
346             else:
347               res = get_make_output_parallel (targ_list, "all-gcc", 0)
348             rc = res[0]
349             if rc != 0:
350               message = "Compilation failed on TARGET : " + res[2]
351               keep = True
352             else:
353               message = "Passed host and target builds"
355         if keep:
356           print message + "\n"
358         if (rc != 0):
359           if verbose:
360             verbose.write (message + "\n");
361             verbose.write (res[1])
362             verbose.write ("\n");
363             if os.path.exists (inc_file):
364               ilog = open(inc_file+".log","a")
365               ilog.write (message + " for " + src_file + ":\n\n");
366               ilog.write ("============================================\n");
367               ilog.write (res[1])
368               ilog.write ("\n");
369               ilog.close()
370             if os.path.exists (src_file):
371               ilog = open(src_file+".log","a")
372               ilog.write (message + " for " +inc_file + ":\n\n");
373               ilog.write ("============================================\n");
374               ilog.write (res[1])
375               ilog.write ("\n");
376               ilog.close()
378         # Given a sequence where :
379         # #include "tm.h"
380         # #include "target.h"  // includes tm.h
382         # target.h was required, and when attempting to remove tm.h we'd see that
383         # all the macro defintions are "required" since they all look like:
384         # #ifndef HAVE_blah
385         # #define HAVE_blah
386         # endif
388         # when target.h was found to be required, tm.h will be tagged as included.
389         # so when we get this far, we know we dont have to check the macros for
390         # tm.h since we know it is already been included.
392         if inc_file_norm not in header_seen:
393           iinfo = get_header_info (inc_file, spath)
394           newlist = all_headers (inc_file_norm)
395           if ii_path(iinfo) == "build" and not target_dict:
396             keep = True
397             text = message + " : Will not remove a build file without some targets."
398             print text
399             ilog = open(src_file+".log","a")
400             ilog.write (text +"\n")
401             ilog.write ("============================================\n");
402             ilog.close()
403             ilog = open("reduce-headers-kept.log","a")
404             ilog.write (src_file + " " + text +"\n")
405             ilog.close()
406         else:
407           newlist = list()
408         if not keep and inc_file_norm not in header_seen:
409           # now look for any macro requirements.
410           for h in newlist:
411             if not h in header_seen:
412               if header_dict.get(h):
413                 defined = ii_macro_define (header_dict[h])
414                 for dep in defined:
415                   if dep in src_requires and dep not in ignore_list:
416                     keep = True;
417                     text = message + ", but must keep " + inc_file + " because it provides " + dep 
418                     if because.get(dep) != None:
419                       text = text + " Possibly required by " + because[dep]
420                     print text
421                     ilog = open(inc_file+".log","a")
422                     ilog.write (because[dep]+": Requires [dep] in "+src_file+"\n")
423                     ilog.write ("============================================\n");
424                     ilog.close()
425                     ilog = open(src_file+".log","a")
426                     ilog.write (text +"\n")
427                     ilog.write ("============================================\n");
428                     ilog.close()
429                     ilog = open("reduce-headers-kept.log","a")
430                     ilog.write (src_file + " " + text +"\n")
431                     ilog.close()
432                     if verbose:
433                       verbose.write (text + "\n")
435         if keep:
436           # add all headers 'consumes' to src_requires list, and mark as seen
437           for h in newlist:
438             if not h in header_seen:
439               header_seen.append (h)
440               if header_dict.get(h):
441                 consume = ii_macro_consume (header_dict[h])
442                 for dep in consume:
443                   if dep not in src_requires:
444                     src_requires.add (dep)
445                     if because.get(dep) == None:
446                       because[dep] = inc_file
448           src_tmp = copy.deepcopy (src_data)
449         else:
450           print message + "  --> removing " + inc_file + "\n"
451           rmcount += 1
452           if verbose:
453             verbose.write (message + "  --> removing " + inc_file + "\n")
454           if remove_count.get(inc_file) == None:
455             remove_count[inc_file] = 1
456           else:
457             remove_count[inc_file] += 1
458           src_data = copy.deepcopy (src_tmp)
459     except:
460       print "Interuption: restoring original file"
461       out = open(src_file, "w")
462       for line in src_orig:
463         out.write (line)
464       out.close()
465       raise
467     # copy current version, since it is the "right" one now.
468     out = open(src_file, "w")
469     for line in src_data:
470       out.write (line)
471     out.close()
472     
473     # Try a final host bootstrap build to make sure everything is kosher.
474     if hostbuild:
475       res = get_make_output (build_dir, "all")
476       rc = res[0]
477       if (rc != 0):
478         # host build failed! return to original version
479         print "Error: " + src_file + " Failed to bootstrap at end!!! restoring."
480         print "        Bad version at " + src_file + ".bad"
481         os.rename (src_file, src_file + ".bad")
482         out = open(src_file, "w")
483         for line in src_orig:
484           out.write (line)
485         out.close()
486         return src_file + ": failed to build after reduction.  Restored original"
488     if src_data == src_orig:
489       summary = src_file + ": No change."
490     else:
491       summary = src_file + ": Reduction performed, "+str(rmcount)+" includes removed."
492   print summary
493   return summary
495 only_h = list ()
496 ignore_cond = False
498 usage = False
499 src = list()
500 only_targs = list ()
501 for x in sys.argv[1:]:
502   if x[0:2] == "-b":
503     build_dir = x[2:]
504   elif x[0:2] == "-f":
505     fn = normalize_header (x[2:])
506     if fn not in only_h:
507       only_h.append (fn)
508   elif x[0:2] == "-h":
509     usage = True
510   elif x[0:2] == "-d":
511     ignore_cond = True
512   elif x[0:2] == "-D":
513     ignore_list.append(x[2:])
514   elif x[0:2] == "-T":
515     only_targs.append(x[2:])
516   elif x[0:2] == "-t":
517     target_dir = x[2:]
518   elif x[0] == "-":
519     print "Error:  Unrecognized option " + x
520     usgae = True
521   else:
522     if not os.path.exists (x):
523       print "Error: specified file " + x + " does not exist."
524       usage = True
525     else:
526       src.append (x)
528 if target_dir:
529   build_target_dict (target_dir, only_targs)
531 if build_dir == "" and target_dir == "":
532   print "Error: Must specify a build directory, and/or a target directory."
533   usage = True
535 if build_dir and not os.path.exists (build_dir):
536     print "Error: specified build directory does not exist : " + build_dir
537     usage = True
539 if target_dir and not os.path.exists (target_dir):
540     print "Error: specified target directory does not exist : " + target_dir
541     usage = True
543 if usage:
544   print "Attempts to remove extraneous include files from source files."
545   print " "
546   print "Should be run from the main gcc source directory, and works on a target"
547   print "directory, as we attempt to make the 'all' target."
548   print " "
549   print "By default, gcc-reorder-includes is run on each file before attempting"
550   print "to remove includes. this removes duplicates and puts some headers in a"
551   print "canonical ordering"
552   print " "
553   print "The build directory should be ready to compile via make. Time is saved"
554   print "if the build is already complete, so that only changes need to be built."
555   print " "
556   print "Usage: [options] file1.c [file2.c] ... [filen.c]"
557   print "      -bdir    : the root build directory to attempt buiding .o files."
558   print "      -tdir    : the target build directory"
559   print "      -d       : Ignore conditional macro dependencies."
560   print " "
561   print "      -Dmacro  : Ignore a specific macro for dependencies"
562   print "      -Ttarget : Only consider target in target directory."
563   print "      -fheader : Specifies a specific .h file to be considered."
564   print " "
565   print "      -D, -T, and -f can be specified mulitple times and are aggregated."
566   print " "
567   print "  The original file will be in filen.bak"
568   print " "
569   sys.exit (0)
571 if only_h:
572   print "Attempting to remove only these files:"
573   for x in only_h:
574     print x
575   print " "
577 logfile = open("reduce-headers.log","w")
579 for x in src:
580   msg = try_to_remove (x, only_h, logfile)
581   ilog = open("reduce-headers.sum","a")
582   ilog.write (msg + "\n")
583   ilog.close()
585 ilog = open("reduce-headers.sum","a")
586 ilog.write ("===============================================================\n")
587 for x in remove_count:
588   msg = x + ": Removed " + str(remove_count[x]) + " times."
589   print msg
590   logfile.write (msg + "\n")
591   ilog.write (msg + "\n")