1#
2# File      : utils.py
3# This file is part of RT-Thread RTOS
4# COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team
5#
6#  This program is free software; you can redistribute it and/or modify
7#  it under the terms of the GNU General Public License as published by
8#  the Free Software Foundation; either version 2 of the License, or
9#  (at your option) any later version.
10#
11#  This program is distributed in the hope that it will be useful,
12#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14#  GNU General Public License for more details.
15#
16#  You should have received a copy of the GNU General Public License along
17#  with this program; if not, write to the Free Software Foundation, Inc.,
18#  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19#
20# Change Logs:
21# Date           Author       Notes
22# 2015-01-20     Bernard      Add copyright information
23# 2024-04-21     Bernard      Add ImportModule to import local module
24
25import sys
26import os
27import re
28
29def splitall(loc):
30    """
31    Return a list of the path components in loc. (Used by relpath_).
32
33    The first item in the list will be  either ``os.curdir``, ``os.pardir``, empty,
34    or the root directory of loc (for example, ``/`` or ``C:\\).
35
36    The other items in the list will be strings.
37
38    Adapted from *path.py* by Jason Orendorff.
39    """
40    parts = []
41    while loc != os.curdir and loc != os.pardir:
42        prev = loc
43        loc, child = os.path.split(prev)
44        if loc == prev:
45            break
46        parts.append(child)
47    parts.append(loc)
48    parts.reverse()
49    return parts
50
51def _make_path_relative(origin, dest):
52    """
53    Return the relative path between origin and dest.
54
55    If it's not possible return dest.
56
57
58    If they are identical return ``os.curdir``
59
60    Adapted from `path.py <http://www.jorendorff.com/articles/python/path/>`_ by Jason Orendorff.
61    """
62    origin = os.path.abspath(origin).replace('\\', '/')
63    dest = os.path.abspath(dest).replace('\\', '/')
64    #
65    orig_list = splitall(os.path.normcase(origin))
66    # Don't normcase dest!  We want to preserve the case.
67    dest_list = splitall(dest)
68    #
69    if orig_list[0] != os.path.normcase(dest_list[0]):
70        # Can't get here from there.
71        return dest
72    #
73    # Find the location where the two paths start to differ.
74    i = 0
75    for start_seg, dest_seg in zip(orig_list, dest_list):
76        if start_seg != os.path.normcase(dest_seg):
77            break
78        i += 1
79    #
80    # Now i is the point where the two paths diverge.
81    # Need a certain number of "os.pardir"s to work up
82    # from the origin to the point of divergence.
83    segments = [os.pardir] * (len(orig_list) - i)
84    # Need to add the diverging part of dest_list.
85    segments += dest_list[i:]
86    if len(segments) == 0:
87        # If they happen to be identical, use os.curdir.
88        return os.curdir
89    else:
90        # return os.path.join(*segments).replace('\\', '/')
91        return os.path.join(*segments)
92
93def xml_indent(elem, level=0):
94    i = "\n" + level*"  "
95    if len(elem):
96        if not elem.text or not elem.text.strip():
97            elem.text = i + "  "
98        if not elem.tail or not elem.tail.strip():
99            elem.tail = i
100        for elem in elem:
101            xml_indent(elem, level+1)
102        if not elem.tail or not elem.tail.strip():
103            elem.tail = i
104    else:
105        if level and (not elem.tail or not elem.tail.strip()):
106            elem.tail = i
107
108
109source_ext = ["c", "h", "s", "S", "cpp", "cxx", "cc", "xpm"]
110source_list = []
111
112def walk_children(child):
113    global source_list
114    global source_ext
115
116    # print child
117    full_path = child.rfile().abspath
118    file_type_list  = full_path.rsplit('.',1)
119    #print file_type
120    if (len(file_type_list) > 1):
121        file_type = full_path.rsplit('.',1)[1]
122
123        if file_type in source_ext:
124            if full_path not in source_list:
125                source_list.append(full_path)
126
127    children = child.all_children()
128    if children != []:
129        for item in children:
130            walk_children(item)
131
132def PrefixPath(prefix, path):
133    path = os.path.abspath(path)
134    prefix = os.path.abspath(prefix)
135
136    if sys.platform == 'win32':
137        prefix = prefix.lower()
138        path = path.lower()
139
140    if path.startswith(prefix):
141        return True
142
143    return False
144
145def ListMap(l):
146    ret_list = []
147    for item in l:
148        if type(item) == type(()):
149            ret = ListMap(item)
150            ret_list += ret
151        elif type(item) == type([]):
152            ret = ListMap(item)
153            ret_list += ret
154        else:
155            ret_list.append(item)
156
157    return ret_list
158
159def TargetGetList(env, postfix):
160    global source_ext
161    global source_list
162
163    target = env['target']
164
165    source_ext = postfix
166    for item in target:
167        walk_children(item)
168
169    source_list.sort()
170
171    return source_list
172
173def ProjectInfo(env):
174
175    project  = env['project']
176    RTT_ROOT = env['RTT_ROOT']
177    BSP_ROOT = env['BSP_ROOT']
178
179    FILES       = []
180    DIRS        = []
181    HEADERS     = []
182    CPPPATH     = []
183    CPPDEFINES  = []
184
185    for group in project:
186        # get each files
187        if 'src' in group and group['src']:
188            FILES += group['src']
189
190        # get each include path
191        if 'CPPPATH' in group and group['CPPPATH']:
192            CPPPATH += group['CPPPATH']
193
194    if 'CPPDEFINES' in env:
195        CPPDEFINES = env['CPPDEFINES']
196        CPPDEFINES = ListMap(CPPDEFINES)
197
198    # process FILES and DIRS
199    if len(FILES):
200        # use absolute path
201        for i in range(len(FILES)):
202            FILES[i] = os.path.abspath(str(FILES[i]))
203            DIRS.append(os.path.dirname(FILES[i]))
204
205        FILES.sort()
206        DIRS = list(set(DIRS))
207        DIRS.sort()
208
209    # process HEADERS
210    HEADERS = TargetGetList(env, ['h'])
211
212    # process CPPPATH
213    if len(CPPPATH):
214        # use absolute path
215        for i in range(len(CPPPATH)):
216            CPPPATH[i] = os.path.abspath(CPPPATH[i])
217
218        # remove repeat path
219        paths = []
220        for p in CPPPATH:
221            if p not in paths:
222                paths.append(p)
223
224        CPPPATH = []
225        for path in paths:
226            if PrefixPath(RTT_ROOT, path):
227                CPPPATH += [os.path.abspath(path).replace('\\', '/')]
228
229            elif PrefixPath(BSP_ROOT, path):
230                CPPPATH += [os.path.abspath(path).replace('\\', '/')]
231
232            else:
233                CPPPATH += ['"%s",' % path.replace('\\', '/')]
234
235    # process CPPDEFINES
236    if len(CPPDEFINES):
237        CPPDEFINES = [i for i in set(CPPDEFINES)]
238
239        CPPDEFINES.sort()
240
241    proj = {}
242    proj['FILES']       = FILES
243    proj['DIRS']        = DIRS
244    proj['HEADERS']     = HEADERS
245    proj['CPPPATH']     = CPPPATH
246    proj['CPPDEFINES']  = CPPDEFINES
247
248    return proj
249
250def VersionCmp(ver1, ver2):
251    la=[]
252    if ver1:
253        la = re.split("[. ]", ver1)
254    lb = re.split("[. ]", ver2)
255
256    f = 0
257    if len(la) > len(lb):
258        f = len(la)
259    else:
260        f = len(lb)
261    for i in range(f):
262        try:
263            if int(la[i]) > int(lb[i]):
264                return 1
265            elif int(la[i]) == int(lb[i]):
266                continue
267            else:
268                return -1
269        except (IndexError, ValueError) as e:
270            if len(la) > len(lb):
271                return 1
272            else:
273                return -1
274    return 0
275
276def GCCC99Patch(cflags):
277    import building
278    gcc_version = building.GetDepend('GCC_VERSION_STR')
279    if gcc_version:
280        gcc_version = gcc_version.replace('"', '')
281    if VersionCmp(gcc_version, "4.8.0") == 1:
282        # remove -std=c99 after GCC 4.8.x
283        cflags = cflags.replace('-std=c99', '')
284
285    return cflags
286
287def ReloadModule(module):
288    import sys
289    if sys.version_info.major >= 3:
290        import importlib
291        importlib.reload(module)
292    else:
293        reload(module)
294
295def ImportModule(module):
296    import sys
297    if sys.version_info.major >= 3:
298        import importlib.util
299        path = os.path.dirname(__file__)
300        spec = importlib.util.spec_from_file_location(module, os.path.join(path, module+".py"))
301        module = importlib.util.module_from_spec(spec)
302        spec.loader.exec_module(module)
303        return module
304    else:
305        return __import__(module, fromlist=[module])
306
307def VerTuple(version_str):
308    ver_parts = version_str.split('.')
309    ver = tuple(int(part) for part in ver_parts)
310
311    return ver
312
313def CmdExists(cmd):
314    # Check if the path directly points to an existing file.
315    if os.path.isfile(cmd):
316        return True
317    else:
318        # On Windows systems, check for common script file extensions
319        # if the file does not exist as specified.
320        if sys.platform.startswith('win'):
321            # Loop through possible extensions to cover cases where the extension is omitted in the input.
322            for ext in ['.exe', '.bat', '.ps1']:
323                # Append the extension to the command path and check if this file exists.
324                if os.path.isfile(cmd + ext):
325                    return True
326
327    # If none of the checks confirm the file exists, return False.
328    return False
329