source: LMDZ6/trunk/libf/phylmd/ecrad-acc/mkhelper/fortmodcmp.py

Last change on this file was 6016, checked in by yann meurdesoif, 3 months ago

Add new ecrad version from DWD ported onto OpenACC, closed from original ecrad ECMWF starting point for LMDZ ecrad version.

Modification from ecrad-lmdz version has been included.

YM

  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 7.3 KB
Line 
1#!/usr/bin/env python
2import sys
3
4BUF_MAX_SIZE = 512
5
6
7def _skip_sequence(stream, sequence):
8    """
9    Finds the first occurrence of a sequence of bytes in a binary stream and
10    sets the streams's current position right after it. Returns True if the
11    sequence is found and False otherwise, The length of the sequence must not
12    exceed BUF_MAX_SIZE.
13    """
14    sequence_size = len(sequence)
15    while 1:
16        buf = stream.read(BUF_MAX_SIZE)
17        idx = buf.find(sequence)
18        if idx < 0:
19            if len(buf) < BUF_MAX_SIZE:
20                return False
21            else:
22                stream.seek(1 - sequence_size, 1)
23        else:
24            stream.seek(idx + sequence_size - len(buf), 1)
25            return True
26
27
28def _mods_differ_default(stream1, stream2):
29    # Simple byte comparison:
30    while 1:
31        buf1 = stream1.read(BUF_MAX_SIZE)
32        buf2 = stream2.read(BUF_MAX_SIZE)
33        if buf1 != buf2:
34            return True
35        if not buf1:
36            return False
37
38
39def _mods_differ_intel(stream1, stream2):
40    # The first byte encodes the version of the module file format:
41    if stream1.read(1) != stream2.read(1):
42        return True
43    # The block before the following magic sequence might change from
44    # compilation to compilation, probably due to a second resolution timestamp
45    # in it:
46    magic_sequence = b'\x0A\x00'  # the same as \n\0
47    if not (_skip_sequence(stream1, magic_sequence) and
48            _skip_sequence(stream2, magic_sequence)):
49        return True
50    return _mods_differ_default(stream1, stream2)
51
52
53def _mods_differ_gnu(stream1, stream2):
54    # The magic number of gzip to be found in the module files generated by
55    # GFortran 4.9 or later:
56    magic_sequence = b'\x1F\x8B'
57    stream1_sequence = stream1.read(len(magic_sequence))
58    stream1.seek(0)
59    if stream1_sequence != magic_sequence:
60        # Older versions of GFortran generate module files in plain ASCII. Also,
61        # up to version 4.6.4, the first line of a module file contains a
62        # timestamp, therefore we ignore it.
63        stream1.readline()
64        stream2.readline()
65    return _mods_differ_default(stream1, stream2)
66
67
68def _mods_differ_portland(stream1, stream2):
69    for _ in range(2):  # the first two lines must be identical
70        if stream1.readline() != stream2.readline():
71            return True
72    # The next line is a timestamp followed by the sequence '\nenduse\n':
73    magic_sequence = b'\x0A\x65\x6E\x64\x75\x73\x65\x0A'
74    if not (_skip_sequence(stream1, magic_sequence) and
75            _skip_sequence(stream2, magic_sequence)):
76        return True
77    return _mods_differ_default(stream1, stream2)
78
79
80def _mods_differ_amd(stream1, stream2):
81    # AOCC is based on the Classic Flang, which has the same format as the PGI
82    # compiler
83    return _mods_differ_portland(stream1, stream2)
84
85
86def _mods_differ_flang(stream1, stream2):
87    # The header of the module files generated by the new Flang compiler,
88    # formerly known as F18:
89    magic_sequence = (b'\xEF\xBB\xBF\x21'   # UTF-8 BOM
90                      b'\x6D\x6F\x64\x24')  # the same as !mod$
91    stream1_sequence = stream1.read(len(magic_sequence))
92    stream1.seek(0)
93    if stream1_sequence != magic_sequence:
94        # The Classic Flang has the same format as the PGI compiler
95        return _mods_differ_portland(stream1, stream2)
96    return _mods_differ_default(stream1, stream2)
97
98
99def _mods_differ_omni(stream1, stream2):
100    import xml.etree.ElementTree as eT
101    # Attributes that either declare or reference the type hashes. Each list
102    # contains a group of tags that reference "same things".
103    hash_attrs = [["imported_id"],
104                  ["type", "ref", "return_type", "extends"]]
105
106    tree1 = eT.parse(stream1)
107    tree2 = eT.parse(stream2)
108
109    try:
110        it1 = tree1.iter()
111        it2 = tree2.iter()
112    except AttributeError:
113        it1 = iter(tree1.getiterator())
114        it2 = iter(tree2.getiterator())
115
116    type_maps1 = [dict() for _ in hash_attrs]
117    type_maps2 = [dict() for _ in hash_attrs]
118
119    for node1 in it1:
120        try:
121            node2 = next(it2)
122        except StopIteration:
123            # The second file is shorter:
124            return True
125
126        if node1.tag != node2.tag:
127            # The nodes have different tags:
128            return True
129
130        if node1.text != node2.text:
131            # The nodes have different texts:
132            return True
133
134        for ii, attr_group in enumerate(hash_attrs):
135            type_map1 = type_maps1[ii]
136            type_map2 = type_maps2[ii]
137
138            for attr in attr_group:
139                if (attr in node1.attrib) != (attr in node2.attrib):
140                    # One of the files has the attribute and the second one
141                    # does not:
142                    return True
143
144                hash1 = node1.attrib.pop(attr, None)
145                hash2 = node2.attrib.pop(attr, None)
146
147                if hash1 == hash2:
148                    # Either the attribute is missing in both nodes or they have
149                    # the same value:
150                    continue
151                elif (hash1 in type_map1) != (hash2 in type_map2):
152                    # One of the files has already declared the respective hash
153                    # and the second one has not:
154                    return True
155                elif hash1 in type_map1 and \
156                        type_map1[hash1] != type_map2[hash2]:
157                    # Both files have declared the respective hashes but they
158                    # refer to different types:
159                    return True
160                else:
161                    # Declare the respective hashes for both files:
162                    type_value = len(type_map1)
163                    type_map1[hash1] = type_value
164                    type_map2[hash2] = type_value
165
166        if node1.attrib != node2.attrib:
167            # The rest of the attributes have different values:
168            return True
169    try:
170        next(it2)
171        # The first file is shorter:
172        return True
173    except StopIteration:
174        return False
175
176
177def mods_differ(filename1, filename2, compiler_name=None):
178    """
179    Checks whether two Fortran module files are essentially different. Some
180    compiler-specific logic is required for compilers that generate different
181    module files for the same source file (e.g. the module files might contain
182    timestamps). This implementation is inspired by CMake.
183    """
184    with open(filename1, 'rb') as stream1:
185        with open(filename2, 'rb') as stream2:
186            if compiler_name == "intel":
187                return _mods_differ_intel(stream1, stream2)
188            elif compiler_name == "gnu":
189                return _mods_differ_gnu(stream1, stream2)
190            elif compiler_name == "portland":
191                return _mods_differ_portland(stream1, stream2)
192            elif compiler_name == "amd":
193                return _mods_differ_amd(stream1, stream2)
194            elif compiler_name == "flang":
195                return _mods_differ_flang(stream1, stream2)
196            elif compiler_name == "omni":
197                return _mods_differ_omni(stream1, stream2)
198            else:
199                return _mods_differ_default(stream1, stream2)
200
201
202# We try to make this as fast as possible, therefore we do not parse arguments
203# properly:
204exit(mods_differ(sys.argv[1], sys.argv[2],
205                 sys.argv[3].lower() if len(sys.argv) > 3 else None))
Note: See TracBrowser for help on using the repository browser.