xref: /netbsd-src/external/gpl3/gcc.old/dist/contrib/update-copyright.py (revision 82d56013d7b633d116a93943de88e08335357a7c)
1#!/usr/bin/python
2#
3# Copyright (C) 2013-2018 Free Software Foundation, Inc.
4#
5# This script is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 3, or (at your option)
8# any later version.
9
10# This script adjusts the copyright notices at the top of source files
11# so that they have the form:
12#
13#   Copyright XXXX-YYYY Free Software Foundation, Inc.
14#
15# It doesn't change code that is known to be maintained elsewhere or
16# that carries a non-FSF copyright.
17#
18# The script also doesn't change testsuite files, except those in
19# libstdc++-v3.  This is because libstdc++-v3 has a conformance testsuite,
20# while most tests in other directories are just things that failed at some
21# point in the past.
22#
23# Pass --this-year to the script if you want it to add the current year
24# to all applicable notices.  Pass --quilt if you are using quilt and
25# want files to be added to the quilt before being changed.
26#
27# By default the script will update all directories for which the
28# output has been vetted.  You can instead pass the names of individual
29# directories, including those that haven't been approved.  So:
30#
31#    update-copyright.py --this-year
32#
33# is the command that would be used at the beginning of a year to update
34# all copyright notices (and possibly at other times to check whether
35# new files have been added with old years).  On the other hand:
36#
37#    update-copyright.py --this-year libitm
38#
39# would run the script on just libitm/.
40#
41# Note that things like --version output strings must be updated before
42# this script is run.  There's already a separate procedure for that.
43
44import os
45import re
46import sys
47import time
48import subprocess
49
50class Errors:
51    def __init__ (self):
52        self.num_errors = 0
53
54    def report (self, filename, string):
55        if filename:
56            string = filename + ': ' + string
57        sys.stderr.write (string + '\n')
58        self.num_errors += 1
59
60    def ok (self):
61        return self.num_errors == 0
62
63class GenericFilter:
64    def __init__ (self):
65        self.skip_files = set()
66        self.skip_dirs = set()
67        self.skip_extensions = set()
68        self.fossilised_files = set()
69        self.own_files = set()
70
71        self.skip_files |= set ([
72                # Skip licence files.
73                'COPYING',
74                'COPYING.LIB',
75                'COPYING3',
76                'COPYING3.LIB',
77                'LICENSE',
78                'fdl.texi',
79                'gpl_v3.texi',
80                'fdl-1.3.xml',
81                'gpl-3.0.xml',
82
83                # Skip auto- and libtool-related files
84                'aclocal.m4',
85                'compile',
86                'config.guess',
87                'config.sub',
88                'depcomp',
89                'install-sh',
90                'libtool.m4',
91                'ltmain.sh',
92                'ltoptions.m4',
93                'ltsugar.m4',
94                'ltversion.m4',
95                'lt~obsolete.m4',
96                'missing',
97                'mkdep',
98                'mkinstalldirs',
99                'move-if-change',
100                'shlibpath.m4',
101                'symlink-tree',
102                'ylwrap',
103
104                # Skip FSF mission statement, etc.
105                'gnu.texi',
106                'funding.texi',
107                'appendix_free.xml',
108
109                # Skip imported texinfo files.
110                'texinfo.tex',
111                ])
112
113
114    def get_line_filter (self, dir, filename):
115        if filename.startswith ('ChangeLog'):
116            # Ignore references to copyright in changelog entries.
117            return re.compile ('\t')
118
119        return None
120
121    def skip_file (self, dir, filename):
122        if filename in self.skip_files:
123            return True
124
125        (base, extension) = os.path.splitext (os.path.join (dir, filename))
126        if extension in self.skip_extensions:
127            return True
128
129        if extension == '.in':
130            # Skip .in files produced by automake.
131            if os.path.exists (base + '.am'):
132                return True
133
134            # Skip files produced by autogen
135            if (os.path.exists (base + '.def')
136                and os.path.exists (base + '.tpl')):
137                return True
138
139        # Skip configure files produced by autoconf
140        if filename == 'configure':
141            if os.path.exists (base + '.ac'):
142                return True
143            if os.path.exists (base + '.in'):
144                return True
145
146        return False
147
148    def skip_dir (self, dir, subdir):
149        return subdir in self.skip_dirs
150
151    def is_fossilised_file (self, dir, filename):
152        if filename in self.fossilised_files:
153            return True
154        # Only touch current current ChangeLogs.
155        if filename != 'ChangeLog' and filename.find ('ChangeLog') >= 0:
156            return True
157        return False
158
159    def by_package_author (self, dir, filename):
160        return filename in self.own_files
161
162class Copyright:
163    def __init__ (self, errors):
164        self.errors = errors
165
166        # Characters in a range of years.  Include '.' for typos.
167        ranges = '[0-9](?:[-0-9.,\s]|\s+and\s+)*[0-9]'
168
169        # Non-whitespace characters in a copyright holder's name.
170        name = '[\w.,-]'
171
172        # Matches one year.
173        self.year_re = re.compile ('[0-9]+')
174
175        # Matches part of a year or copyright holder.
176        self.continuation_re = re.compile (ranges + '|' + name)
177
178        # Matches a full copyright notice:
179        self.copyright_re = re.compile (
180            # 1: 'Copyright (C)', etc.
181            '([Cc]opyright'
182            '|[Cc]opyright\s+\([Cc]\)'
183            '|[Cc]opyright\s+%s'
184            '|[Cc]opyright\s+©'
185            '|[Cc]opyright\s+@copyright{}'
186            '|copyright = u\''
187            '|@set\s+copyright[\w-]+)'
188
189            # 2: the years.  Include the whitespace in the year, so that
190            # we can remove any excess.
191            '(\s*(?:' + ranges + ',?'
192            '|@value\{[^{}]*\})\s*)'
193
194            # 3: 'by ', if used
195            '(by\s+)?'
196
197            # 4: the copyright holder.  Don't allow multiple consecutive
198            # spaces, so that right-margin gloss doesn't get caught
199            # (e.g. gnat_ugn.texi).
200            '(' + name + '(?:\s?' + name + ')*)?')
201
202        # A regexp for notices that might have slipped by.  Just matching
203        # 'copyright' is too noisy, and 'copyright.*[0-9]' falls foul of
204        # HTML header markers, so check for 'copyright' and two digits.
205        self.other_copyright_re = re.compile ('copyright.*[0-9][0-9]',
206                                              re.IGNORECASE)
207        self.comment_re = re.compile('#+|[*]+|;+|%+|//+|@c |dnl ')
208        self.holders = { '@copying': '@copying' }
209        self.holder_prefixes = set()
210
211        # True to 'quilt add' files before changing them.
212        self.use_quilt = False
213
214        # If set, force all notices to include this year.
215        self.max_year = None
216
217        # Goes after the year(s).  Could be ', '.
218        self.separator = ' '
219
220    def add_package_author (self, holder, canon_form = None):
221        if not canon_form:
222            canon_form = holder
223        self.holders[holder] = canon_form
224        index = holder.find (' ')
225        while index >= 0:
226            self.holder_prefixes.add (holder[:index])
227            index = holder.find (' ', index + 1)
228
229    def add_external_author (self, holder):
230        self.holders[holder] = None
231
232    class BadYear():
233        def __init__ (self, year):
234            self.year = year
235
236        def __str__ (self):
237            return 'unrecognised year: ' + self.year
238
239    def parse_year (self, string):
240        year = int (string)
241        if len (string) == 2:
242            if year > 70:
243                return year + 1900
244        elif len (string) == 4:
245            return year
246        raise self.BadYear (string)
247
248    def year_range (self, years):
249        year_list = [self.parse_year (year)
250                     for year in self.year_re.findall (years)]
251        assert len (year_list) > 0
252        return (min (year_list), max (year_list))
253
254    def set_use_quilt (self, use_quilt):
255        self.use_quilt = use_quilt
256
257    def include_year (self, year):
258        assert not self.max_year
259        self.max_year = year
260
261    def canonicalise_years (self, dir, filename, filter, years):
262        # Leave texinfo variables alone.
263        if years.startswith ('@value'):
264            return years
265
266        (min_year, max_year) = self.year_range (years)
267
268        # Update the upper bound, if enabled.
269        if self.max_year and not filter.is_fossilised_file (dir, filename):
270            max_year = max (max_year, self.max_year)
271
272        # Use a range.
273        if min_year == max_year:
274            return '%d' % min_year
275        else:
276            return '%d-%d' % (min_year, max_year)
277
278    def strip_continuation (self, line):
279        line = line.lstrip()
280        match = self.comment_re.match (line)
281        if match:
282            line = line[match.end():].lstrip()
283        return line
284
285    def is_complete (self, match):
286        holder = match.group (4)
287        return (holder
288                and (holder not in self.holder_prefixes
289                     or holder in self.holders))
290
291    def update_copyright (self, dir, filename, filter, file, line, match):
292        orig_line = line
293        next_line = None
294        pathname = os.path.join (dir, filename)
295
296        intro = match.group (1)
297        if intro.startswith ('@set'):
298            # Texinfo year variables should always be on one line
299            after_years = line[match.end (2):].strip()
300            if after_years != '':
301                self.errors.report (pathname,
302                                    'trailing characters in @set: '
303                                    + after_years)
304                return (False, orig_line, next_line)
305        else:
306            # If it looks like the copyright is incomplete, add the next line.
307            while not self.is_complete (match):
308                try:
309                    next_line = file.next()
310                except StopIteration:
311                    break
312
313                # If the next line doesn't look like a proper continuation,
314                # assume that what we've got is complete.
315                continuation = self.strip_continuation (next_line)
316                if not self.continuation_re.match (continuation):
317                    break
318
319                # Merge the lines for matching purposes.
320                orig_line += next_line
321                line = line.rstrip() + ' ' + continuation
322                next_line = None
323
324                # Rematch with the longer line, at the original position.
325                match = self.copyright_re.match (line, match.start())
326                assert match
327
328            holder = match.group (4)
329
330            # Use the filter to test cases where markup is getting in the way.
331            if filter.by_package_author (dir, filename):
332                assert holder not in self.holders
333
334            elif not holder:
335                self.errors.report (pathname, 'missing copyright holder')
336                return (False, orig_line, next_line)
337
338            elif holder not in self.holders:
339                self.errors.report (pathname,
340                                    'unrecognised copyright holder: ' + holder)
341                return (False, orig_line, next_line)
342
343            else:
344                # See whether the copyright is associated with the package
345                # author.
346                canon_form = self.holders[holder]
347                if not canon_form:
348                    return (False, orig_line, next_line)
349
350                # Make sure the author is given in a consistent way.
351                line = (line[:match.start (4)]
352                        + canon_form
353                        + line[match.end (4):])
354
355                # Remove any 'by'
356                line = line[:match.start (3)] + line[match.end (3):]
357
358        # Update the copyright years.
359        years = match.group (2).strip()
360        try:
361            canon_form = self.canonicalise_years (dir, filename, filter, years)
362        except self.BadYear as e:
363            self.errors.report (pathname, str (e))
364            return (False, orig_line, next_line)
365
366        line = (line[:match.start (2)]
367                + ('' if intro.startswith ('copyright = ') else ' ')
368                + canon_form + self.separator
369                + line[match.end (2):])
370
371        # Use the standard (C) form.
372        if intro.endswith ('right'):
373            intro += ' (C)'
374        elif intro.endswith ('(c)'):
375            intro = intro[:-3] + '(C)'
376        line = line[:match.start (1)] + intro + line[match.end (1):]
377
378        # Strip trailing whitespace
379        line = line.rstrip() + '\n'
380
381        return (line != orig_line, line, next_line)
382
383    def process_file (self, dir, filename, filter):
384        pathname = os.path.join (dir, filename)
385        if filename.endswith ('.tmp'):
386            # Looks like something we tried to create before.
387            try:
388                os.remove (pathname)
389            except OSError:
390                pass
391            return
392
393        lines = []
394        changed = False
395        line_filter = filter.get_line_filter (dir, filename)
396        mode = None
397        with open (pathname, 'r') as file:
398            prev = None
399            mode = os.fstat (file.fileno()).st_mode
400            for line in file:
401                while line:
402                    next_line = None
403                    # Leave filtered-out lines alone.
404                    if not (line_filter and line_filter.match (line)):
405                        match = self.copyright_re.search (line)
406                        if match:
407                            res = self.update_copyright (dir, filename, filter,
408                                                         file, line, match)
409                            (this_changed, line, next_line) = res
410                            changed = changed or this_changed
411
412                        # Check for copyright lines that might have slipped by.
413                        elif self.other_copyright_re.search (line):
414                            self.errors.report (pathname,
415                                                'unrecognised copyright: %s'
416                                                % line.strip())
417                    lines.append (line)
418                    line = next_line
419
420        # If something changed, write the new file out.
421        if changed and self.errors.ok():
422            tmp_pathname = pathname + '.tmp'
423            with open (tmp_pathname, 'w') as file:
424                for line in lines:
425                    file.write (line)
426                os.fchmod (file.fileno(), mode)
427            if self.use_quilt:
428                subprocess.call (['quilt', 'add', pathname])
429            os.rename (tmp_pathname, pathname)
430
431    def process_tree (self, tree, filter):
432        for (dir, subdirs, filenames) in os.walk (tree):
433            # Don't recurse through directories that should be skipped.
434            for i in xrange (len (subdirs) - 1, -1, -1):
435                if filter.skip_dir (dir, subdirs[i]):
436                    del subdirs[i]
437
438            # Handle the files in this directory.
439            for filename in filenames:
440                if filter.skip_file (dir, filename):
441                    sys.stdout.write ('Skipping %s\n'
442                                      % os.path.join (dir, filename))
443                else:
444                    self.process_file (dir, filename, filter)
445
446class CmdLine:
447    def __init__ (self, copyright = Copyright):
448        self.errors = Errors()
449        self.copyright = copyright (self.errors)
450        self.dirs = []
451        self.default_dirs = []
452        self.chosen_dirs = []
453        self.option_handlers = dict()
454        self.option_help = []
455
456        self.add_option ('--help', 'Print this help', self.o_help)
457        self.add_option ('--quilt', '"quilt add" files before changing them',
458                         self.o_quilt)
459        self.add_option ('--this-year', 'Add the current year to every notice',
460                         self.o_this_year)
461
462    def add_option (self, name, help, handler):
463        self.option_help.append ((name, help))
464        self.option_handlers[name] = handler
465
466    def add_dir (self, dir, filter = GenericFilter()):
467        self.dirs.append ((dir, filter))
468
469    def o_help (self, option = None):
470        sys.stdout.write ('Usage: %s [options] dir1 dir2...\n\n'
471                          'Options:\n' % sys.argv[0])
472        format = '%-15s %s\n'
473        for (what, help) in self.option_help:
474            sys.stdout.write (format % (what, help))
475        sys.stdout.write ('\nDirectories:\n')
476
477        format = '%-25s'
478        i = 0
479        for (dir, filter) in self.dirs:
480            i += 1
481            if i % 3 == 0 or i == len (self.dirs):
482                sys.stdout.write (dir + '\n')
483            else:
484                sys.stdout.write (format % dir)
485        sys.exit (0)
486
487    def o_quilt (self, option):
488        self.copyright.set_use_quilt (True)
489
490    def o_this_year (self, option):
491        self.copyright.include_year (time.localtime().tm_year)
492
493    def main (self):
494        for arg in sys.argv[1:]:
495            if arg[:1] != '-':
496                self.chosen_dirs.append (arg)
497            elif arg in self.option_handlers:
498                self.option_handlers[arg] (arg)
499            else:
500                self.errors.report (None, 'unrecognised option: ' + arg)
501        if self.errors.ok():
502            if len (self.chosen_dirs) == 0:
503                self.chosen_dirs = self.default_dirs
504            if len (self.chosen_dirs) == 0:
505                self.o_help()
506            else:
507                for chosen_dir in self.chosen_dirs:
508                    canon_dir = os.path.join (chosen_dir, '')
509                    count = 0
510                    for (dir, filter) in self.dirs:
511                        if (dir + os.sep).startswith (canon_dir):
512                            count += 1
513                            self.copyright.process_tree (dir, filter)
514                    if count == 0:
515                        self.errors.report (None, 'unrecognised directory: '
516                                            + chosen_dir)
517        sys.exit (0 if self.errors.ok() else 1)
518
519#----------------------------------------------------------------------------
520
521class TopLevelFilter (GenericFilter):
522    def skip_dir (self, dir, subdir):
523        return True
524
525class ConfigFilter (GenericFilter):
526    def __init__ (self):
527        GenericFilter.__init__ (self)
528
529    def skip_file (self, dir, filename):
530        if filename.endswith ('.m4'):
531            pathname = os.path.join (dir, filename)
532            with open (pathname) as file:
533                # Skip files imported from gettext.
534                if file.readline().find ('gettext-') >= 0:
535                    return True
536        return GenericFilter.skip_file (self, dir, filename)
537
538class GCCFilter (GenericFilter):
539    def __init__ (self):
540        GenericFilter.__init__ (self)
541
542        self.skip_files |= set ([
543                # Not part of GCC
544                'math-68881.h',
545                ])
546
547        self.skip_dirs |= set ([
548                # Better not create a merge nightmare for the GNAT folks.
549                'ada',
550
551                # Handled separately.
552                'testsuite',
553                ])
554
555        self.skip_extensions |= set ([
556                # Maintained by the translation project.
557                '.po',
558
559                # Automatically-generated.
560                '.pot',
561                ])
562
563        self.fossilised_files |= set ([
564                # Old news won't be updated.
565                'ONEWS',
566                ])
567
568class TestsuiteFilter (GenericFilter):
569    def __init__ (self):
570        GenericFilter.__init__ (self)
571
572        self.skip_extensions |= set ([
573                # Don't change the tests, which could be woend by anyone.
574                '.c',
575                '.C',
576                '.cc',
577                '.d',
578                '.h',
579                '.hs',
580                '.f',
581                '.f90',
582                '.go',
583                '.inc',
584                '.java',
585                ])
586
587    def skip_file (self, dir, filename):
588        # g++.niklas/README contains historical copyright information
589        # and isn't updated.
590        if filename == 'README' and os.path.basename (dir) == 'g++.niklas':
591            return True
592        # Similarly params/README.
593        if filename == 'README' and os.path.basename (dir) == 'params':
594            return True
595        if filename == 'pdt_5.f03' and os.path.basename (dir) == 'gfortran.dg':
596	    return True
597        return GenericFilter.skip_file (self, dir, filename)
598
599class LibCppFilter (GenericFilter):
600    def __init__ (self):
601        GenericFilter.__init__ (self)
602
603        self.skip_extensions |= set ([
604                # Maintained by the translation project.
605                '.po',
606
607                # Automatically-generated.
608                '.pot',
609                ])
610
611class LibGCCFilter (GenericFilter):
612    def __init__ (self):
613        GenericFilter.__init__ (self)
614
615        self.skip_dirs |= set ([
616                # Imported from GLIBC.
617                'soft-fp',
618                ])
619
620class LibPhobosFilter (GenericFilter):
621    def __init__ (self):
622        GenericFilter.__init__ (self)
623
624        self.skip_files |= set ([
625                # Source module imported from upstream.
626                'object.d',
627                ])
628
629        self.skip_dirs |= set ([
630                # Contains sources imported from upstream.
631                'core',
632                'etc',
633                'gc',
634                'gcstub',
635                'rt',
636                'std',
637                ])
638
639class LibStdCxxFilter (GenericFilter):
640    def __init__ (self):
641        GenericFilter.__init__ (self)
642
643        self.skip_files |= set ([
644                # Contains no copyright of its own, but quotes the GPL.
645                'intro.xml',
646                ])
647
648        self.skip_dirs |= set ([
649                # Contains automatically-generated sources.
650                'html',
651
652                # The testsuite data files shouldn't be changed.
653                'data',
654
655                # Contains imported images
656                'images',
657                ])
658
659        self.own_files |= set ([
660                # Contains markup around the copyright owner.
661                'spine.xml',
662                ])
663
664    def get_line_filter (self, dir, filename):
665        if filename == 'boost_concept_check.h':
666            return re.compile ('// \(C\) Copyright Jeremy Siek')
667        return GenericFilter.get_line_filter (self, dir, filename)
668
669class GCCCopyright (Copyright):
670    def __init__ (self, errors):
671        Copyright.__init__ (self, errors)
672
673        canon_fsf = 'Free Software Foundation, Inc.'
674        self.add_package_author ('Free Software Foundation', canon_fsf)
675        self.add_package_author ('Free Software Foundation.', canon_fsf)
676        self.add_package_author ('Free Software Foundation Inc.', canon_fsf)
677        self.add_package_author ('Free Software Foundation, Inc', canon_fsf)
678        self.add_package_author ('Free Software Foundation, Inc.', canon_fsf)
679        self.add_package_author ('The Free Software Foundation', canon_fsf)
680        self.add_package_author ('The Free Software Foundation, Inc.', canon_fsf)
681        self.add_package_author ('Software Foundation, Inc.', canon_fsf)
682
683        self.add_external_author ('ARM')
684        self.add_external_author ('AdaCore')
685        self.add_external_author ('Ami Tavory and Vladimir Dreizin, IBM-HRL.')
686        self.add_external_author ('Cavium Networks.')
687        self.add_external_author ('Faraday Technology Corp.')
688        self.add_external_author ('Florida State University')
689        self.add_external_author ('Gerard Jungman')
690        self.add_external_author ('Greg Colvin and Beman Dawes.')
691        self.add_external_author ('Hewlett-Packard Company')
692        self.add_external_author ('Intel Corporation')
693        self.add_external_author ('Information Technology Industry Council.')
694        self.add_external_author ('James Theiler, Brian Gough')
695        self.add_external_author ('Makoto Matsumoto and Takuji Nishimura,')
696        self.add_external_author ('National Research Council of Canada.')
697        self.add_external_author ('NVIDIA Corporation')
698        self.add_external_author ('Peter Dimov and Multi Media Ltd.')
699        self.add_external_author ('Peter Dimov')
700        self.add_external_author ('Pipeline Associates, Inc.')
701        self.add_external_author ('Regents of the University of California.')
702        self.add_external_author ('Silicon Graphics Computer Systems, Inc.')
703        self.add_external_author ('Silicon Graphics')
704        self.add_external_author ('Stephen L. Moshier')
705        self.add_external_author ('Sun Microsystems, Inc. All rights reserved.')
706        self.add_external_author ('The D Language Foundation, All Rights Reserved')
707        self.add_external_author ('The Go Authors.  All rights reserved.')
708        self.add_external_author ('The Go Authors. All rights reserved.')
709        self.add_external_author ('The Go Authors.')
710        self.add_external_author ('The Regents of the University of California.')
711        self.add_external_author ('Unicode, Inc.')
712        self.add_external_author ('University of Toronto.')
713
714class GCCCmdLine (CmdLine):
715    def __init__ (self):
716        CmdLine.__init__ (self, GCCCopyright)
717
718        self.add_dir ('.', TopLevelFilter())
719        # boehm-gc is imported from upstream.
720        self.add_dir ('config', ConfigFilter())
721        # contrib isn't really part of GCC.
722        self.add_dir ('fixincludes')
723        self.add_dir ('gcc', GCCFilter())
724        self.add_dir (os.path.join ('gcc', 'testsuite'), TestsuiteFilter())
725        self.add_dir ('gnattools')
726        self.add_dir ('gotools')
727        self.add_dir ('include')
728        # intl is imported from upstream.
729        self.add_dir ('libada')
730        self.add_dir ('libatomic')
731        self.add_dir ('libbacktrace')
732        self.add_dir ('libcc1')
733        self.add_dir ('libcpp', LibCppFilter())
734        self.add_dir ('libdecnumber')
735        # libffi is imported from upstream.
736        self.add_dir ('libgcc', LibGCCFilter())
737        self.add_dir ('libgfortran')
738        # libgo is imported from upstream.
739        self.add_dir ('libgomp')
740        self.add_dir ('libhsail-rt')
741        self.add_dir ('libiberty')
742        self.add_dir ('libitm')
743        self.add_dir ('libobjc')
744        # liboffloadmic is imported from upstream.
745        self.add_dir ('libphobos', LibPhobosFilter())
746        self.add_dir ('libquadmath')
747        # libsanitizer is imported from upstream.
748        self.add_dir ('libssp')
749        self.add_dir ('libstdc++-v3', LibStdCxxFilter())
750        self.add_dir ('libvtv')
751        self.add_dir ('lto-plugin')
752        # maintainer-scripts maintainer-scripts
753        # zlib is imported from upstream.
754
755        self.default_dirs = [
756            'gcc',
757            'include',
758            'libada',
759            'libatomic',
760            'libbacktrace',
761            'libcc1',
762            'libcpp',
763            'libdecnumber',
764            'libgcc',
765            'libgfortran',
766            'libgomp',
767            'libhsail-rt',
768            'libiberty',
769            'libitm',
770            'libobjc',
771            'libphobos',
772            'libssp',
773            'libstdc++-v3',
774            'libvtv',
775            'lto-plugin',
776            ]
777
778GCCCmdLine().main()
779