Initial support for ATmega328P (Arduino Duemilanove).
[bertos.git] / wizard / BToolchainPage.py
1 #!/usr/bin/env python
2 # encoding: utf-8
3 #
4 # This file is part of BeRTOS.
5 #
6 # Bertos 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
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 #
20 # As a special exception, you may use this file as part of a free software
21 # library without restriction.  Specifically, if other files instantiate
22 # templates or use macros or inline functions from this file, or you compile
23 # this file and link it with other files to produce an executable, this
24 # file does not by itself cause the resulting executable to be covered by
25 # the GNU General Public License.  This exception does not however
26 # invalidate any other reasons why the executable file might be covered by
27 # the GNU General Public License.
28 #
29 # Copyright 2008 Develer S.r.l. (http://www.develer.com/)
30 #
31 # $Id$
32 #
33 # Author: Lorenzo Berni <duplo@develer.com>
34 #
35
36 import os
37 import collections
38
39 from BWizardPage import *
40 import BToolchainSearch
41 import bertos_utils
42 import qvariant_converter
43
44 from const import *
45
46 class BToolchainPage(BWizardPage):
47     """
48     Page of the wizard that permits to choose the toolchain to use for the
49     project.
50     """
51
52     def __init__(self):
53         BWizardPage.__init__(self, UI_LOCATION + "/toolchain_select.ui")
54         self.setTitle(self.tr("Select toolchain"))
55         self._validation_process = None
56         self._valid_items = []
57
58     ## Overloaded QWizardPage methods. ##
59
60     def isComplete(self):
61         """
62         Overload of the QWizard isComplete method.
63         """
64         if self.pageContent.toolchainList.currentRow() != -1:
65             self.setProjectInfo("TOOLCHAIN",
66                 qvariant_converter.getStringDict(self.pageContent.toolchainList.currentItem().data(Qt.UserRole)))
67             return True
68         else:
69             return False
70
71     ####
72
73     ## Overloaded BWizardPage methods. ##
74
75     def setupUi(self):
76         """
77         Sets up the user interface.
78         """
79         self.pageContent.infoLabel.setVisible(False)
80
81     def connectSignals(self):
82         """
83         Connects the signals with the related slots.
84         """
85         self.connect(self.pageContent.toolchainList, SIGNAL("itemSelectionChanged()"), self.selectionChanged)
86         self.connect(self.pageContent.addButton, SIGNAL("clicked()"), self.addToolchain)
87         self.connect(self.pageContent.removeButton, SIGNAL("clicked()"), self.removeToolchain)
88         self.connect(self.pageContent.searchButton, SIGNAL("clicked()"), self.searchToolchain)
89         self.connect(self.pageContent.checkButton, SIGNAL("clicked()"), self.validateAllToolchains)
90
91     def reloadData(self):
92         """
93         Overload of the BWizard reloadData method.
94         """
95         self._clearList()
96         self.setupUi()
97         self._populateToolchainList()
98         if len(self._valid_items) == 1:
99             self.pageContent.toolchainList.setCurrentItem(self._valid_items[0])
100
101     ####
102
103     ## Slots ##
104
105     def selectionChanged(self):
106         """
107         Slot called when the user click on an entry of the toolchain list.
108         """
109         if self.pageContent.toolchainList.currentRow() != -1:
110             infos = collections.defaultdict(lambda: unicode("not defined"))
111             infos.update(qvariant_converter.getStringDict(self.pageContent.toolchainList.currentItem().data(Qt.UserRole)))
112             self.pageContent.infoLabel.setText("GCC " + infos["version"] + " (" + infos["build"] + ")\nTarget: " + infos["target"] + "\nPath: " + os.path.normpath(infos["path"]))
113             self.pageContent.infoLabel.setVisible(True)
114             if self.isDefaultToolchain(infos):
115                 self.disableRemoveButton()
116             else:
117                 self.enableRemoveButton()
118             self.emit(SIGNAL("completeChanged()"))
119
120     def addToolchain(self):
121         """
122         Slot called when the user adds manually a toolchain.
123         """
124         sel_toolchain = unicode(QFileDialog.getOpenFileName(self, self.tr("Choose the toolchain"), ""))
125         if sel_toolchain != "":
126             item = QListWidgetItem(sel_toolchain)
127             item.setData(Qt.UserRole, qvariant_converter.convertStringDict({"path": sel_toolchain}))
128             self.pageContent.toolchainList.addItem(item)
129             toolchains = self.toolchains()
130             toolchains[sel_toolchain] = False
131             self.setToolchains(toolchains)
132
133     def removeToolchain(self):
134         """
135         Slot called when the user removes manually a toolchain.
136         """
137         if self.pageContent.toolchainList.currentRow() != -1:
138             item = self.pageContent.toolchainList.takeItem(self.pageContent.toolchainList.currentRow())
139             toolchain = qvariant_converter.getStringDict(item.data(Qt.UserRole))["path"]
140             toolchains = self.toolchains()
141             del toolchains[toolchain]
142             self.setToolchains(toolchains)
143
144     def searchToolchain(self):
145         """
146         Slot called when the user clicks on the 'search' button. It opens the
147         toolchain search dialog.
148         """
149         search = BToolchainSearch.BToolchainSearch()
150         self.connect(search, SIGNAL("accepted()"), self._search)
151         search.exec_()
152
153     def validateAllToolchains(self):
154         """
155         Slot called when the user clicks on the validate button. It starts the
156         toolchain validation procedure for all the toolchains.
157         """
158         QApplication.instance().setOverrideCursor(Qt.WaitCursor)
159         for i in range(self.pageContent.toolchainList.count()):
160             self.validateToolchain(i)
161         QApplication.instance().restoreOverrideCursor()
162
163     ####
164
165     def _populateToolchainList(self):
166         """
167         Fills the toolchain list with the toolchains stored in the QSettings.
168         """
169         toolchains = self.toolchains()
170         if os.name == "nt":
171             import winreg_importer
172             stored_toolchains = winreg_importer.getBertosToolchains()
173             for toolchain in stored_toolchains:
174                 toolchains[toolchain] = True
175         sel_toolchain = self.projectInfo("TOOLCHAIN")
176         for key, value in toolchains.items():
177             if os.path.exists(key):
178                 item = QListWidgetItem(key)
179                 item.setData(Qt.UserRole, qvariant_converter.convertStringDict({"path": key}))
180                 self.pageContent.toolchainList.addItem(item)
181                 if sel_toolchain and sel_toolchain["path"] == key:
182                     self.pageContent.toolchainList.setCurrentItem(item)
183                 if value:
184                     self.validateToolchain(self.pageContent.toolchainList.row(item))
185
186     def currentToolchain(self):
187         selected_toolchain = qvariant_converter.getStringDict(self.pageContent.toolchainList.currentItem().data(Qt.UserRole))
188         return selected_toolchain
189
190     def _clearList(self):
191         """
192         Removes all the toolchain from the list.
193         """
194         self.pageContent.toolchainList.clear()
195
196     def _search(self):
197         """
198         Searches for toolchains in the stored directories, and stores them in the
199         QSettings.
200         """
201         dir_list = self.searchDirList()
202         if self.pathSearch():
203             dir_list += [element for element in bertos_utils.getSystemPath()]
204         toolchain_list = bertos_utils.findToolchains(dir_list)
205         stored_toolchains = self.toolchains()
206         for element in toolchain_list:
207             if not element in stored_toolchains:
208                 item = QListWidgetItem(element)
209                 item.setData(Qt.UserRole, qvariant_converter.convertStringDict({"path": element}))
210                 self.pageContent.toolchainList.addItem(item)
211                 stored_toolchains[element] = False
212         self.setToolchains(stored_toolchains)
213         self.showMessage(self.tr("Toolchain search result."), self.tr("%1 toolchains founded").arg(len(stored_toolchains)))
214
215     def _validItem(self, index, infos):
216         """
217         Sets the item at index as a valid item and associates the given info to it.
218         """
219         item = self.pageContent.toolchainList.item(index)
220         new_data = qvariant_converter.getStringDict(self.pageContent.toolchainList.item(index).data(Qt.UserRole))
221         new_data.update(infos)
222         item.setData(Qt.UserRole, qvariant_converter.convertStringDict(new_data))
223         needed = self.projectInfo("CPU_INFOS")
224         if "target" in infos and infos["target"].find(needed["TOOLCHAIN"]) != -1:
225             item.setIcon(QIcon(":/images/ok.png"))
226             self._valid_items.append(item)
227         else:
228             item.setIcon(QIcon(":/images/warning.png"))
229         if "version" in infos and "target" in infos:
230             item.setText("GCC " + infos["version"] + " - " + infos["target"].strip())
231
232     def _invalidItem(self, index):
233         """
234         Sets the item at index as an invalid item.
235         """
236         item = self.pageContent.toolchainList.item(index)
237         item.setIcon(QIcon(":/images/error.png"))
238
239     def validateToolchain(self, i):
240         """
241         Toolchain validation procedure.
242         """
243         filename = qvariant_converter.getStringDict(self.pageContent.toolchainList.item(i).data(Qt.UserRole))["path"]
244         valid = False
245         info = {}
246         # Check for the other tools of the toolchain
247         for tool in TOOLCHAIN_ITEMS:
248             if os.path.exists(filename.replace("gcc", tool)):
249                 valid = True
250             else:
251                 valid = False
252                 break
253         # Try to retrieve the informations about the toolchain only for the valid toolchains
254         if valid:
255             self._validation_process = QProcess()
256             self._validation_process.start(filename, ["-v"])
257             self._validation_process.waitForStarted(1000)
258             if self._validation_process.waitForFinished(200):
259                 description = unicode(self._validation_process.readAllStandardError())
260                 info = bertos_utils.getToolchainInfo(description)
261                 if len(info) >= 4:
262                     valid = True
263             else:
264                 self._validation_process.kill()
265         # Add the item in the list with the appropriate associate data.
266         if valid:
267             self._validItem(i, info)
268         else:
269             self._invalidItem(i)
270         toolchains = self.toolchains()
271         toolchains[filename] = True
272         self.setToolchains(toolchains)
273     
274     def isDefaultToolchain(self, toolchain):
275         """
276         Returns True if the given toolchain is one of the default toolchains.
277         """
278         if os.name == "nt":
279             import winreg_importer
280             stored_toolchains = winreg_importer.getBertosToolchains()
281             if toolchain["path"] in stored_toolchains:
282                 return True
283         return False
284     
285     def disableRemoveButton(self):
286         """
287         Disable the remove button.
288         """
289         self.pageContent.removeButton.setEnabled(False)
290     
291     def enableRemoveButton(self):
292         """
293         Enable the remove button.
294         """
295         self.pageContent.removeButton.setEnabled(True)
296         
297     def currentItem(self):
298         return self.pageContent.toolchainList.currentItem()