eclipse.py 12.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
#
# Copyright (c) 2006-2019, RT-Thread Development Team
#
# SPDX-License-Identifier: Apache-2.0
#
# Change Logs:
# Date           Author       Notes
# 2019-03-21     Bernard      the first version
# 2019-04-15     armink       fix project update error
#

B
Bernard Xiong 已提交
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
import os
import sys
import glob

from utils import *
from utils import _make_path_relative
from utils import xml_indent

import xml.etree.ElementTree as etree
from xml.etree.ElementTree import SubElement

source_pattern = ['*.c', '*.cpp', '*.cxx', '*.s', '*.S', '*.asm']

def OSPath(path):
    import platform

    if type(path) == type('str'):
        if platform.system() == 'Windows':
            return path.replace('/', '\\')
        else:
            return path.replace('\\', '/')
    else:
        if platform.system() == 'Windows':
            return [item.replace('/', '\\') for item in path]
        else:
            return [item.replace('\\', '/') for item in path]

39 40

# collect the build source code path and parent path
B
Bernard Xiong 已提交
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
def CollectPaths(paths):
    all_paths = []

    def ParentPaths(path):
        ret = os.path.dirname(path)
        if ret == path or ret == '':
            return []

        return [ret] + ParentPaths(ret)

    for path in paths:
        # path = os.path.abspath(path)
        path = path.replace('\\', '/')
        all_paths = all_paths + [path] + ParentPaths(path)

    all_paths = list(set(all_paths))
    return sorted(all_paths)

'''
Collect all of files under paths
'''
def CollectFiles(paths, pattern):
    files = []
    for path in paths:
        if type(pattern) == type(''):
            files = files + glob.glob(path + '/' + pattern)
        else:
            for item in pattern:
                # print('--> %s' % (path + '/' + item))
                files = files + glob.glob(path + '/' + item)

    return sorted(files)

def CollectAllFilesinPath(path, pattern):
    files = []

    for item in pattern:
        files += glob.glob(path + '/' + item)

    list = os.listdir(path)
    if len(list):
        for item in list:
            if item.startswith('.'):
                continue
            if item == 'bsp':
                continue

            if os.path.isdir(os.path.join(path, item)):
                files = files + CollectAllFilesinPath(os.path.join(path, item), pattern)
    return files

'''
Exclude files from infiles
'''
def ExcludeFiles(infiles, files):
    in_files  = set([OSPath(file) for file in infiles])
    exl_files = set([OSPath(file) for file in files])

    exl_files = in_files - exl_files

    return exl_files

103 104

# caluclate the exclude path for project
105
def ExcludePaths(rootpath, paths):
B
Bernard Xiong 已提交
106 107
    ret = []

108
    files = os.listdir(rootpath)
B
Bernard Xiong 已提交
109 110 111 112
    for file in files:
        if file.startswith('.'):
            continue

113
        fullname = os.path.join(rootpath, file)
B
Bernard Xiong 已提交
114 115 116 117 118 119 120 121 122 123

        if os.path.isdir(fullname):
            # print(fullname)
            if not fullname in paths:
                ret = ret + [fullname]
            else:
                ret = ret + ExcludePaths(fullname, paths)

    return ret

124 125 126 127 128 129 130

def ConverToEclipsePathFormat(path):
    if path.startswith('.'):
        path = path[1:]
    return '"${workspace_loc:/${ProjName}/' + path + '}"'


131
def HandleToolOption(tools, env, project, reset):
B
Bernard Xiong 已提交
132 133 134
    BSP_ROOT = os.path.abspath(env['BSP_ROOT'])

    CPPDEFINES = project['CPPDEFINES']
135
    paths = [ConverToEclipsePathFormat(RelativeProjectPath(env, os.path.normpath(i)).replace('\\', '/')) for i in project['CPPPATH']]
B
Bernard Xiong 已提交
136 137 138 139 140

    for tool in tools:
        if tool.get('id').find('c.compile') != 1:
            options = tool.findall('option')
            for option in options:
E
eddylin83 已提交
141
                if option.get('id').find('c.compiler.include.paths') != -1 or option.get('id').find('c.compiler.option.includepaths') != -1:
B
Bernard Xiong 已提交
142 143 144 145
                    # find all of paths in this project
                    include_paths = option.findall('listOptionValue')
                    project_paths = []
                    for item in include_paths:
146 147 148 149 150
                        if reset is True:
                            # clean all old configuration
                            option.remove(item)
                        else:
                            project_paths += [item.get('value')]
B
Bernard Xiong 已提交
151 152 153 154 155 156 157 158 159 160 161

                    if len(project_paths) > 0:
                        cproject_paths = set(paths) - set(project_paths)
                    else:
                        cproject_paths = paths

                    # print('c.compiler.include.paths')
                    cproject_paths = sorted(cproject_paths)
                    for item in cproject_paths:
                        SubElement(option, 'listOptionValue', {'builtIn': 'false', 'value': item})

E
eddylin83 已提交
162
                if option.get('id').find('c.compiler.defs') != -1 or option.get('id').find('c.compiler.option.definedsymbols') != -1:
B
Bernard Xiong 已提交
163 164 165
                    defs = option.findall('listOptionValue')
                    project_defs = []
                    for item in defs:
166 167 168 169 170
                        if reset is True:
                            # clean all old configuration
                            option.remove(item)
                        else:
                            project_defs += [item.get('value')]
B
Bernard Xiong 已提交
171 172 173 174 175 176 177 178 179 180 181 182 183
                    if len(project_defs) > 0:
                        cproject_defs = set(CPPDEFINES) - set(project_defs)
                    else:
                        cproject_defs = CPPDEFINES

                    # print('c.compiler.defs')
                    cproject_defs = sorted(cproject_defs)
                    for item in cproject_defs:
                        SubElement(option, 'listOptionValue', {'builtIn': 'false', 'value': item})

        if tool.get('id').find('c.linker') != -1:
            options = tool.findall('option')
            for option in options:
184
                # update linker script config
B
Bernard Xiong 已提交
185 186 187 188 189
                if option.get('id').find('c.linker.scriptfile') != -1:
                    linker_script = 'link.lds'
                    items = env['LINKFLAGS'].split(' ')
                    if '-T' in items:
                        linker_script = items[items.index('-T') + 1]
190
                        linker_script = ConverToEclipsePathFormat(linker_script)
B
Bernard Xiong 已提交
191 192 193 194 195 196 197

                    listOptionValue = option.find('listOptionValue')
                    if listOptionValue != None:
                        listOptionValue.set('value', linker_script)
                    else:
                        SubElement(option, 'listOptionValue', {'builtIn': 'false', 'value': linker_script})

E
eddylin83 已提交
198 199 200 201 202 203 204
                # scriptfile in stm32cubeIDE        
                if option.get('id').find('c.linker.option.script') != -1:
                    items = env['LINKFLAGS'].split(' ')
                    if '-T' in items:
                        linker_script = ConverToEclipsePathFormat(items[items.index('-T') + 1]).strip('"')
                        option.set('value',linker_script)

205
                # update nostartfiles config
B
Bernard Xiong 已提交
206 207 208 209 210 211
                if option.get('id').find('c.linker.nostart') != -1:
                    if env['LINKFLAGS'].find('-nostartfiles') != -1:
                        option.set('value', 'true')
                    else:
                        option.set('value', 'false')

212 213 214 215 216 217 218 219 220
                # update libs
                if option.get('id').find('c.linker.libs') != -1 and env.has_key('LIBS'):
                    # remove old libs
                    for item in option.findall('listOptionValue'):
                        option.remove(item)
                    # add new libs
                    for lib in env['LIBS']:
                        SubElement(option, 'listOptionValue', {'builtIn': 'false', 'value': lib})

B
Bernard Xiong 已提交
221 222
    return

223 224

def UpdateProjectStructure(env, prj_name):
B
Bernard Xiong 已提交
225 226 227
    bsp_root = env['BSP_ROOT']
    rtt_root = env['RTT_ROOT']

228 229
    project = etree.parse('.project')
    root = project.getroot()
B
Bernard Xiong 已提交
230

231 232 233 234
    if rtt_root.startswith(bsp_root):
        linkedResources = root.find('linkedResources')
        if linkedResources == None:
            linkedResources = SubElement(root, 'linkedResources')
B
Bernard Xiong 已提交
235

236 237 238 239 240
        links = linkedResources.findall('link')
        # delete all RT-Thread folder links
        for link in links:
            if link.find('name').text.startswith('rt-thread'):
                linkedResources.remove(link)
241

242 243 244 245 246 247 248 249 250 251 252
    if prj_name:
        name = root.find('name')
        if name == None:
            name = SubElement(root, 'name')
        name.text = prj_name

    out = open('.project', 'w')
    out.write('<?xml version="1.0" encoding="UTF-8"?>\n')
    xml_indent(root)
    out.write(etree.tostring(root, encoding = 'utf-8'))
    out.close()
B
Bernard Xiong 已提交
253 254 255

    return

256 257
def GenExcluding(env, project):
    rtt_root = os.path.abspath(env['RTT_ROOT'])
258
    bsp_root = os.path.abspath(env['BSP_ROOT'])
259 260
    coll_dirs = CollectPaths(project['DIRS'])
    all_paths = [OSPath(path) for path in coll_dirs]
B
Bernard Xiong 已提交
261

262 263 264 265 266 267 268 269 270
    if bsp_root.startswith(rtt_root):
        # bsp folder is in the RT-Thread root folder, such as the RT-Thread source code on GitHub
        exclude_paths = ExcludePaths(rtt_root, all_paths)
    elif rtt_root.startswith(bsp_root):
        # RT-Thread root folder is in the bsp folder, such as project folder which generate by 'scons --dist' cmd
        exclude_paths = ExcludePaths(bsp_root, all_paths)
    else:
        exclude_paths = ExcludePaths(rtt_root, all_paths)
        exclude_paths += ExcludePaths(bsp_root, all_paths)
B
Bernard Xiong 已提交
271 272 273

    paths = exclude_paths
    exclude_paths = []
274
    # remove the folder which not has source code by source_pattern
B
Bernard Xiong 已提交
275 276 277 278 279 280 281 282 283 284
    for path in paths:
        # add bsp and libcpu folder and not collect source files (too more files)
        if path.endswith('rt-thread\\bsp') or path.endswith('rt-thread\\libcpu'):
            exclude_paths += [path]
            continue

        set = CollectAllFilesinPath(path, source_pattern)
        if len(set):
            exclude_paths += [path]

285
    exclude_paths = [RelativeProjectPath(env, path).replace('\\', '/') for path in exclude_paths]
B
Bernard Xiong 已提交
286 287 288 289 290 291
    env['ExPaths'] = exclude_paths

    all_files = CollectFiles(all_paths, source_pattern)
    src_files = project['FILES']

    exclude_files = ExcludeFiles(all_files, src_files)
292
    exclude_files = [RelativeProjectPath(env, file).replace('\\', '/') for file in exclude_files]
B
Bernard Xiong 已提交
293
    env['ExFiles'] = exclude_files
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
        
    return  exclude_paths + exclude_files


def RelativeProjectPath(env, path):
    project_root = os.path.abspath(env['BSP_ROOT'])
    rtt_root = os.path.abspath(env['RTT_ROOT'])
    
    if path.startswith(project_root):
        return _make_path_relative(project_root, path)
    
    if path.startswith(rtt_root):
        return 'rt-thread/' + _make_path_relative(rtt_root, path)

    # TODO add others folder
    print('ERROR: the ' + path + 'not support')

    return path


314
def UpdateCproject(env, project, excluding, reset):
315
    excluding = sorted(excluding)
B
Bernard Xiong 已提交
316 317 318 319 320 321 322

    cproject = etree.parse('.cproject')

    root = cproject.getroot()
    cconfigurations = root.findall('storageModule/cconfiguration')
    for cconfiguration in cconfigurations:
        tools = cconfiguration.findall('storageModule/configuration/folderInfo/toolChain/tool')
323
        HandleToolOption(tools, env, project, reset)
B
Bernard Xiong 已提交
324 325 326 327 328 329 330 331 332 333 334 335 336

        sourceEntries = cconfiguration.find('storageModule/configuration/sourceEntries')
        entry = sourceEntries.find('entry')
        if entry != None:
            sourceEntries.remove(entry)

        value = ''
        for item in excluding:
            if value == '':
                value = item
            else:
                value += '|' + item

337
        SubElement(sourceEntries, 'entry', {'excluding': value, 'flags': 'VALUE_WORKSPACE_PATH|RESOLVED', 'kind':'sourcePath', 'name':""})
B
Bernard Xiong 已提交
338 339 340 341 342 343 344 345 346

    # write back to .cproject
    out = open('.cproject', 'w')
    out.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n')
    out.write('<?fileVersion 4.0.0?>')
    xml_indent(root)
    out.write(etree.tostring(root, encoding='utf-8'))
    out.close()

347

348
def TargetEclipse(env, reset = False, prj_name = None):
349 350 351 352 353 354 355 356 357 358 359
    global source_pattern

    print('Update eclipse setting...')

    if not os.path.exists('.cproject'):
        print('no eclipse CDT project found!')
        return

    project = ProjectInfo(env)

    # update the project file structure info on '.project' file
360
    UpdateProjectStructure(env, prj_name)
361 362 363 364 365

    # generate the exclude paths and files
    excluding = GenExcluding(env, project)

    # update the project configuration on '.cproject' file
366
    UpdateCproject(env, project, excluding, reset)
367

B
Bernard Xiong 已提交
368 369 370
    print('done!')

    return