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