]> Git Repo - pico-vscode.git/blob - scripts/pico_project.py
Fix JSON syntax errors
[pico-vscode.git] / scripts / pico_project.py
1 #!/usr/bin/env python3
2
3 #
4 # Copyright (c) 2020-2023 Raspberry Pi (Trading) Ltd.
5 #
6 # SPDX-License-Identifier: BSD-3-Clause
7 #
8
9 import argparse
10 from copy import copy
11 import os
12 from pyexpat import features
13 import shutil
14 from pathlib import Path
15 import string
16 import sys
17 import subprocess
18 import platform
19 import shlex
20 import csv
21
22 import tkinter as tk
23 from tkinter import messagebox as mb
24 from tkinter import filedialog as fd
25 from tkinter import simpledialog as sd
26 from tkinter import ttk
27
28 CMAKELIST_FILENAME = 'CMakeLists.txt'
29 CMAKECACHE_FILENAME = 'CMakeCache.txt'
30
31 COMPILER_NAME = 'arm-none-eabi-gcc'
32
33 VSCODE_LAUNCH_FILENAME = 'launch.json'
34 VSCODE_C_PROPERTIES_FILENAME = 'c_cpp_properties.json'
35 VSCODE_SETTINGS_FILENAME ='settings.json'
36 VSCODE_EXTENSIONS_FILENAME ='extensions.json'
37 VSCODE_FOLDER='.vscode'
38
39 CONFIG_UNSET="Not set"
40
41 # Standard libraries for all builds
42 # And any more to string below, space separator
43 STANDARD_LIBRARIES = 'pico_stdlib'
44
45 # Indexed on feature name, tuple contains the C file, the H file and the CMake project name for the feature. 
46 # Some lists may contain an extra/ancillary file needed for that feature
47 GUI_TEXT = 0
48 C_FILE = 1
49 H_FILE = 2
50 LIB_NAME = 3
51 ANCILLARY_FILE = 4
52
53 features_list = {
54     'spi' :             ("SPI",             "spi.c",            "hardware/spi.h",       "hardware_spi"),
55     'i2c' :             ("I2C interface",   "i2c.c",            "hardware/i2c.h",       "hardware_i2c"),
56     'dma' :             ("DMA support",     "dma.c",            "hardware/dma.h",       "hardware_dma"),
57     'pio' :             ("PIO interface",   "pio.c",            "hardware/pio.h",       "hardware_pio"),
58     'interp' :          ("HW interpolation", "interp.c",        "hardware/interp.h",    "hardware_interp"),
59     'timer' :           ("HW timer",        "timer.c",          "hardware/timer.h",     "hardware_timer"),
60     'watchdog' :        ("HW watchdog",     "watch.c",          "hardware/watchdog.h",  "hardware_watchdog"),
61     'clocks' :          ("HW clocks",       "clocks.c",         "hardware/clocks.h",    "hardware_clocks"),
62 }
63
64 picow_options_list = {
65     'picow_none' :      ("None", "",                            "",    "",                                                                  ""),
66     'picow_led' :       ("PicoW onboard LED", "",               "pico/cyw43_arch.h",    "pico_cyw43_arch_none",                             ""),
67     'picow_poll' :      ("Polled lwIP",     "",                 "pico/cyw43_arch.h",    "pico_cyw43_arch_lwip_poll",                        "lwipopts.h"),
68     'picow_background' :("Background lwIP", "",                 "pico/cyw43_arch.h",    "pico_cyw43_arch_lwip_threadsafe_background",       "lwipopts.h"),
69 #    'picow_freertos' :  ("Full lwIP (FreeRTOS)", "",            "pico/cyw43_arch.h",    "pico_cyw43_arch_lwip_sys_freertos",                "lwipopts.h"),
70 }
71
72 stdlib_examples_list = {
73     'uart':     ("UART",                    "uart.c",           "hardware/uart.h",      "hardware_uart"),
74     'gpio' :    ("GPIO interface",          "gpio.c",           "hardware/gpio.h",      "hardware_gpio"),
75     'div' :     ("Low level HW Divider",    "divider.c",        "hardware/divider.h",   "hardware_divider")
76 }
77
78 debugger_list = ["DebugProbe (CMSIS-DAP)", "SWD (Pi host)"]
79 debugger_config_list = ["cmsis-dap.cfg", "raspberrypi-swd.cfg"]
80
81 DEFINES = 0
82 INITIALISERS = 1
83 # Could add an extra item that shows how to use some of the available functions for the feature
84 #EXAMPLE = 2
85
86 # This also contains example code for the standard library (see stdlib_examples_list)
87 code_fragments_per_feature = {
88     'uart' : [
89                ("// UART defines",
90                 "// By default the stdout UART is `uart0`, so we will use the second one",
91                 "#define UART_ID uart1",
92                 "#define BAUD_RATE 9600", "",
93                 "// Use pins 4 and 5 for UART1",
94                 "// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
95                 "#define UART_TX_PIN 4",
96                 "#define UART_RX_PIN 5" ),
97
98                ( "// Set up our UART",
99                  "uart_init(UART_ID, BAUD_RATE);",
100                  "// Set the TX and RX pins by using the function select on the GPIO",
101                  "// Set datasheet for more information on function select",
102                  "gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);",
103                  "gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);", "" )
104             ],
105     'spi' : [
106               ( "// SPI Defines",
107                 "// We are going to use SPI 0, and allocate it to the following GPIO pins",
108                 "// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
109                 "#define SPI_PORT spi0",
110                 "#define PIN_MISO 16",
111                 "#define PIN_CS   17",
112                 "#define PIN_SCK  18",
113                 "#define PIN_MOSI 19" ),
114
115               ( "// SPI initialisation. This example will use SPI at 1MHz.",
116                 "spi_init(SPI_PORT, 1000*1000);",
117                 "gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);",
118                 "gpio_set_function(PIN_CS,   GPIO_FUNC_SIO);",
119                 "gpio_set_function(PIN_SCK,  GPIO_FUNC_SPI);",
120                 "gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);", "",
121                 "// Chip select is active-low, so we'll initialise it to a driven-high state",
122                 "gpio_set_dir(PIN_CS, GPIO_OUT);",
123                 "gpio_put(PIN_CS, 1);", "")
124             ],
125     'i2c' : [
126               (
127                 "// I2C defines",
128                 "// This example will use I2C0 on GPIO8 (SDA) and GPIO9 (SCL) running at 400KHz.",
129                 "// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
130                 "#define I2C_PORT i2c0",
131                 "#define I2C_SDA 8",
132                 "#define I2C_SCL 9",
133               ),
134               (
135                 "// I2C Initialisation. Using it at 400Khz.",
136                 "i2c_init(I2C_PORT, 400*1000);","",
137                 "gpio_set_function(I2C_SDA, GPIO_FUNC_I2C);",
138                 "gpio_set_function(I2C_SCL, GPIO_FUNC_I2C);",
139                 "gpio_pull_up(I2C_SDA);",
140                 "gpio_pull_up(I2C_SCL);"
141               )
142             ],
143     "gpio" : [
144               (
145                 "// GPIO defines",
146                 "// Example uses GPIO 2",
147                 "#define GPIO 2"
148               ),
149               (
150                 "// GPIO initialisation.",
151                 "// We will make this GPIO an input, and pull it up by default",
152                 "gpio_init(GPIO);",
153                 "gpio_set_dir(GPIO, GPIO_IN);",
154                 "gpio_pull_up(GPIO);","",
155               )
156             ],
157     "interp" :[
158                (),
159                (
160                 "// Interpolator example code",
161                 "interp_config cfg = interp_default_config();",
162                 "// Now use the various interpolator library functions for your use case",
163                 "// e.g. interp_config_clamp(&cfg, true);",
164                 "//      interp_config_shift(&cfg, 2);",
165                 "// Then set the config ",
166                 "interp_set_config(interp0, 0, &cfg);",
167                )
168               ],
169
170     "timer"  : [
171                 (
172                  "int64_t alarm_callback(alarm_id_t id, void *user_data) {",
173                  "    // Put your timeout handler code in here",
174                  "    return 0;",
175                  "}"
176                 ),
177                 (
178                  "// Timer example code - This example fires off the callback after 2000ms",
179                  "add_alarm_in_ms(2000, alarm_callback, NULL, false);"
180                 )
181               ],
182
183     "watchdog":[ (),
184                 (
185                     "// Watchdog example code",
186                     "if (watchdog_caused_reboot()) {",
187                     "    // Whatever action you may take if a watchdog caused a reboot",
188                     "}","",
189                     "// Enable the watchdog, requiring the watchdog to be updated every 100ms or the chip will reboot",
190                     "// second arg is pause on debug which means the watchdog will pause when stepping through code",
191                     "watchdog_enable(100, 1);","",
192                     "// You need to call this function at least more often than the 100ms in the enable call to prevent a reboot"
193                     "watchdog_update();",
194                 )
195               ],
196
197     "div"    : [ (),
198                  (
199                     "// Example of using the HW divider. The pico_divider library provides a more user friendly set of APIs ",
200                     "// over the divider (and support for 64 bit divides), and of course by default regular C language integer",
201                     "// divisions are redirected thru that library, meaning you can just use C level `/` and `%` operators and",
202                     "// gain the benefits of the fast hardware divider.",
203                     "int32_t dividend = 123456;",
204                     "int32_t divisor = -321;",
205                     "// This is the recommended signed fast divider for general use.",
206                     "divmod_result_t result = hw_divider_divmod_s32(dividend, divisor);",
207                     "printf(\"%d/%d = %d remainder %d\\n\", dividend, divisor, to_quotient_s32(result), to_remainder_s32(result));",
208                     "// This is the recommended unsigned fast divider for general use.",
209                     "int32_t udividend = 123456;",
210                     "int32_t udivisor = 321;",
211                     "divmod_result_t uresult = hw_divider_divmod_u32(udividend, udivisor);",
212                     "printf(\"%d/%d = %d remainder %d\\n\", udividend, udivisor, to_quotient_u32(uresult), to_remainder_u32(uresult));"
213                  )
214                 ]
215 }
216
217 configuration_dictionary = list(dict())
218
219 isMac = False
220 isWindows = False
221 compilerPath = Path("/usr/bin/arm-none-eabi-gcc")
222
223 def GetBackground():
224     return 'white'
225
226 def GetButtonBackground():
227     return 'white'
228
229 def GetTextColour():
230     return 'black'
231
232 def GetButtonTextColour():
233     return '#c51a4a'
234
235 def RunGUI(sdkpath, args):
236     root = tk.Tk()
237     style = ttk.Style(root)
238     style.theme_use('default')
239
240     style.configure("TButton", padding=6, relief="groove", border=2, foreground=GetButtonTextColour(), background=GetButtonBackground())
241     style.configure("TLabel", foreground=GetTextColour(), background=GetBackground() )
242     style.configure("TCheckbutton", foreground=GetTextColour(), background=GetBackground())
243     style.configure("TRadiobutton", foreground=GetTextColour(), background=GetBackground() )
244     style.configure("TLabelframe", foreground=GetTextColour(), background=GetBackground() )
245     style.configure("TLabelframe.Label", foreground=GetTextColour(), background=GetBackground() )
246     style.configure("TCombobox", foreground=GetTextColour(), background=GetBackground() )
247     style.configure("TListbox", foreground=GetTextColour(), background=GetBackground() )
248
249     style.map("TCheckbutton", background = [('disabled', GetBackground())])
250     style.map("TRadiobutton", background = [('disabled', GetBackground())])
251     style.map("TButton", background = [('disabled', GetBackground())])
252     style.map("TLabel", background = [('background', GetBackground())])
253     style.map("TComboBox", background = [('readonly', GetBackground())])
254
255     app = ProjectWindow(root, sdkpath, args)
256
257     app.configure(background=GetBackground())
258
259     root.mainloop()
260     sys.exit(0)
261
262 def RunWarning(message):
263     mb.showwarning('Raspberry Pi Pico Project Generator', message)
264     sys.exit(0)
265
266 import threading
267
268 def thread_function(text, command, ok):
269     l = shlex.split(command)
270     proc = subprocess.Popen(l, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
271     for line in iter(proc.stdout.readline,''):
272         if not line:
273             if ok:
274                 ok["state"] = tk.NORMAL
275             return
276         text.insert(tk.END, line)
277         text.see(tk.END)
278
279 # Function to run an OS command and display the output in a new modal window
280 class DisplayWindow(tk.Toplevel):
281     def __init__(self, parent, title):
282         tk.Toplevel.__init__(self, parent)
283         self.parent = parent
284         self.init_window(title)
285
286     def init_window(self, title):
287         self.title(title)
288
289         frame = tk.Frame(self, borderwidth=5, relief=tk.RIDGE)
290         frame.pack(fill=tk.X, expand=True, side=tk.TOP)
291
292         scrollbar = tk.Scrollbar(frame)
293         self.text = tk.Text(frame, bg='gray14', fg='gray99')
294         scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
295         self.text.pack(side=tk.LEFT, fill=tk.Y)
296         scrollbar.config(command=self.text.yview)
297         self.text.config(yscrollcommand=scrollbar.set)
298
299         frame1 = tk.Frame(self, borderwidth=1)
300         frame1.pack(fill=tk.X, expand=True, side=tk.BOTTOM)
301         self.OKButton = ttk.Button(frame1, text="OK", command=self.OK)
302         self.OKButton["state"] = tk.DISABLED
303         self.OKButton.pack()
304
305         # make dialog modal
306         self.transient(self.parent)
307         self.grab_set()
308
309     def OK(self):
310         self.grab_release()
311         self.destroy()
312
313 def RunCommandInWindow(parent, command):
314     w = DisplayWindow(parent, command)
315     x = threading.Thread(target=thread_function, args=(w.text, command, w.OKButton))
316     x.start()
317     parent.wait_window(w)
318
319 class EditBoolWindow(sd.Dialog):
320
321     def __init__(self, parent, configitem, current):
322         self.parent = parent
323         self.config_item = configitem
324         self.current = current
325         sd.Dialog.__init__(self, parent, "Edit boolean configuration")
326
327
328     def body(self, master):
329         self.configure(background=GetBackground())
330         ttk.Label(self, text=self.config_item['name']).pack()
331         self.result = tk.StringVar()
332         self.result.set(self.current)
333         ttk.Radiobutton(master, text="True", variable=self.result, value="True").pack(anchor=tk.W)
334         ttk.Radiobutton(master, text="False", variable=self.result, value="False").pack(anchor=tk.W)
335         ttk.Radiobutton(master, text=CONFIG_UNSET, variable=self.result, value=CONFIG_UNSET).pack(anchor=tk.W)
336
337     def get(self):
338         return self.result.get()
339
340 class EditIntWindow(sd.Dialog):
341
342     def __init__(self, parent, configitem, current):
343         self.parent = parent
344         self.config_item = configitem
345         self.current = current
346         sd.Dialog.__init__(self, parent, "Edit integer configuration")
347
348     def body(self, master):
349         self.configure(background=GetBackground())
350         str = self.config_item['name'] + "  Max = " + self.config_item['max'] + "  Min = " + self.config_item['min']
351         ttk.Label(self, text=str).pack()
352         self.input =  tk.Entry(self)
353         self.input.pack(pady=4)
354         self.input.insert(0, self.current)
355         ttk.Button(self, text=CONFIG_UNSET, command=self.unset).pack(pady=5)
356
357     def validate(self):
358         self.result = self.input.get()
359         # Check for numeric entry
360         return True
361
362     def unset(self):
363         self.result = CONFIG_UNSET
364         self.destroy()
365
366     def get(self):
367         return self.result
368
369 class EditEnumWindow(sd.Dialog):
370     def __init__(self, parent, configitem, current):
371         self.parent = parent
372         self.config_item = configitem
373         self.current = current
374         sd.Dialog.__init__(self, parent, "Edit Enumeration configuration")
375
376     def body(self, master):
377         #self.configure(background=GetBackground())
378         values = self.config_item['enumvalues'].split('|')
379         values.insert(0,'Not set')
380         self.input =  ttk.Combobox(self, values=values, state='readonly')
381         self.input.set(self.current)
382         self.input.pack(pady=12)
383
384     def validate(self):
385         self.result = self.input.get()
386         return True
387
388     def get(self):
389         return self.result
390
391
392 class ConfigurationWindow(tk.Toplevel):
393
394     def __init__(self, parent, initial_config):
395         tk.Toplevel.__init__(self, parent)
396         self.master = parent
397         self.results = initial_config
398         self.init_window(self)
399
400     def init_window(self, args):
401         self.configure(background=GetBackground())
402         self.title("Advanced Configuration")
403         ttk.Label(self, text="Select the advanced options you wish to enable or change. Note that you really should understand the implications of changing these items before using them!").grid(row=0, column=0, columnspan=5)
404         ttk.Label(self, text="Name").grid(row=1, column=0, sticky=tk.W)
405         ttk.Label(self, text="Type").grid(row=1, column=1, sticky=tk.W)
406         ttk.Label(self, text="Min").grid(row=1, column=2, sticky=tk.W)
407         ttk.Label(self, text="Max").grid(row=1, column=3, sticky=tk.W)
408         ttk.Label(self, text="Default").grid(row=1, column=4, sticky=tk.W)
409         ttk.Label(self, text="User").grid(row=1, column=5, sticky=tk.W)
410
411         okButton = ttk.Button(self, text="OK", command=self.ok)
412         cancelButton = ttk.Button(self, text="Cancel", command=self.cancel)
413
414         self.namelist = tk.Listbox(self, selectmode=tk.SINGLE)
415         self.typelist = tk.Listbox(self, selectmode=tk.SINGLE)
416         self.minlist = tk.Listbox(self, selectmode=tk.SINGLE)
417         self.maxlist = tk.Listbox(self, selectmode=tk.SINGLE)
418         self.defaultlist = tk.Listbox(self, selectmode=tk.SINGLE)
419         self.valuelist = tk.Listbox(self, selectmode=tk.SINGLE)
420
421         self.descriptionText = tk.Text(self, state=tk.DISABLED, height=2)
422
423         ## Make a list of our list boxes to make it all easier to handle
424         self.listlist = [self.namelist, self.typelist, self.minlist, self.maxlist, self.defaultlist, self.valuelist]
425
426         scroll = tk.Scrollbar(self, orient=tk.VERTICAL, command=self.yview)
427
428         for box in self.listlist:
429             box.config(width=0)
430             box.config(yscrollcommand=scroll.set)
431             box.bind("<MouseWheel>", self.mousewheel)
432             box.bind("<Button-4>", self.mousewheel)
433             box.bind("<Button-5>", self.mousewheel)
434             box.bind("<<ListboxSelect>>", self.changeSelection)
435             box.bind("<Double-Button>", self.doubleClick)
436             box.config(exportselection=False)
437             box.bind("<Down>", self.OnEntryUpDown)
438             box.bind("<Up>", self.OnEntryUpDown)
439
440         scroll.grid(column=7, sticky=tk.N + tk.S)
441
442         i = 0
443         for box in self.listlist:
444             box.grid(row=2, column=i, padx=0, sticky=tk.W + tk.E)
445             i+=1
446
447         self.descriptionText.grid(row = 3, column=0, columnspan=4, sticky=tk.W + tk.E)
448         cancelButton.grid(column=5, row = 3, padx=5)
449         okButton.grid(column=4, row = 3, sticky=tk.E, padx=5)
450
451         # populate the list box with our config options
452         for conf in configuration_dictionary:
453             self.namelist.insert(tk.END, conf['name'])
454             s = conf['type']
455             if s == "":
456                 s = "int"
457             self.typelist.insert(tk.END, s)
458             self.maxlist.insert(tk.END, conf['max'])
459             self.minlist.insert(tk.END, conf['min'])
460             self.defaultlist.insert(tk.END, conf['default'])
461
462             # see if this config has a setting, our results member has this predefined from init
463             val = self.results.get(conf['name'], CONFIG_UNSET)
464             self.valuelist.insert(tk.END, val)
465             if val != CONFIG_UNSET:
466                 self.valuelist.itemconfig(self.valuelist.size() - 1, {'bg':'green'})
467
468     def yview(self, *args):
469         for box in self.listlist:
470             box.yview(*args)
471
472     def mousewheel(self, event):
473         if (event.num == 4):    # Linux encodes wheel as 'buttons' 4 and 5
474             delta = -1
475         elif (event.num == 5):
476             delta = 1
477         else:                   # Windows & OSX
478             delta = event.delta
479
480         for box in self.listlist:
481             box.yview("scroll", delta, "units")
482         return "break"
483
484     def changeSelection(self, evt):
485         box = evt.widget
486         sellist = box.curselection()
487
488         if sellist:
489             index = int(sellist[0])
490             config = self.namelist.get(index)
491             # Now find the description for that config in the dictionary
492             for conf in configuration_dictionary:
493                 if conf['name'] == config:
494                     self.descriptionText.config(state=tk.NORMAL)
495                     self.descriptionText.delete(1.0,tk.END)
496                     str = config + "\n" + conf['description']
497                     self.descriptionText.insert(1.0, str)
498                     self.descriptionText.config(state=tk.DISABLED)
499                     break
500             # Set all the other list boxes to the same index
501             for b in self.listlist:
502                 if b != box:
503                     b.selection_clear(0, tk.END)
504                     b.selection_set(index)
505
506     def OnEntryUpDown(self, event):
507         box = event.widget
508         selection = box.curselection()
509
510         if selection:
511             index = int(selection[0])
512             if event.keysym == 'Up':
513                 index -= 1
514             elif event.keysym == 'Down':
515                 index += 1
516
517             if 0 <= index < box.size():
518                 for b in self.listlist:
519                         b.selection_clear(0, tk.END)
520                         b.selection_set(index)
521                         b.see(index)
522
523     def doubleClick(self, evt):
524         box = evt.widget
525         index = int(box.curselection()[0])
526         config = self.namelist.get(index)
527         # Get the associated dict entry from our list of configs
528         for conf in configuration_dictionary:
529             if conf['name'] == config:
530                 if (conf['type'] == 'bool'):
531                     result = EditBoolWindow(self, conf, self.valuelist.get(index)).get()
532                 elif (conf['type'] == 'int' or conf['type'] == ""): # "" defaults to int
533                     result = EditIntWindow(self, conf, self.valuelist.get(index)).get()
534                 elif conf['type'] == 'enum':
535                     result = EditEnumWindow(self, conf, self.valuelist.get(index)).get()
536
537                 # Update the valuelist with our new item
538                 self.valuelist.delete(index)
539                 self.valuelist.insert(index, result)
540                 if result != CONFIG_UNSET:
541                     self.valuelist.itemconfig(index, {'bg':'green'})
542                 break
543
544     def ok(self):
545         # Get the selections, and create a list of them
546         for i, val in enumerate(self.valuelist.get(0, tk.END)):
547             if val != CONFIG_UNSET:
548                 self.results[self.namelist.get(i)] = val
549             else:
550                 self.results.pop(self.namelist.get(i), None)
551
552         self.destroy()
553
554     def cancel(self):
555         self.destroy()
556
557     def get(self):
558         return self.results
559
560 class WirelessSettingsWindow(sd.Dialog):
561
562     def __init__(self, parent):
563         sd.Dialog.__init__(self, parent, "Wireless settings")
564         self.parent = parent
565
566     def body(self, master):
567         self.configure(background=GetBackground())
568         master.configure(background=GetBackground())
569         self.ssid = tk.StringVar()
570         self.password = tk.StringVar()
571
572         a = ttk.Label(master, text='SSID :', background=GetBackground())
573         a.grid(row=0, column=0, sticky=tk.E)
574         a.configure(background=GetBackground())
575         ttk.Entry(master, textvariable=self.ssid).grid(row=0, column=1, sticky=tk.W+tk.E, padx=5)
576
577         ttk.Label(master, text='Password :').grid(row=1, column=0, sticky=tk.E)
578         ttk.Entry(master, textvariable=self.password).grid(row=1, column=1, sticky=tk.W+tk.E, padx=5)
579
580         self.transient(self.parent)
581         self.grab_set()
582
583     def ok(self):
584         self.grab_release()
585         self.destroy()
586
587     def cancel(self):
588         self.destroy()
589
590     def get(self):
591         return (self.ssid.get(), self.password.get())
592
593 # Our main window
594 class ProjectWindow(tk.Frame):
595
596     def __init__(self, parent, sdkpath, args):
597         tk.Frame.__init__(self, parent)
598         self.master = parent
599         self.sdkpath = sdkpath
600         self.init_window(args)
601         self.configs = dict()
602         self.ssid = str()
603         self.password = str()
604
605     def setState(self, thing, state):
606         for child in thing.winfo_children():
607             child.configure(state=state)
608
609     def boardtype_change_callback(self, event):
610         boardtype = self.boardtype.get()
611         if boardtype == "pico_w":
612             self.setState(self.picowSubframe, "enabled")
613         else:
614             self.setState(self.picowSubframe, "disabled")
615
616     def wirelessSettings(self):
617         result = WirelessSettingsWindow(self)
618         self.ssid, self.password = result.get()
619
620     def init_window(self, args):
621         self.master.title("Raspberry Pi Pico Project Generator")
622         self.master.configure(bg=GetBackground())
623
624         optionsRow = 0
625
626         mainFrame = tk.Frame(self, bg=GetBackground()).grid(row=optionsRow, column=0, columnspan=6, rowspan=12)
627
628         # Need to keep a reference to the image or it will not appear.
629         self.logo = tk.PhotoImage(file=GetFilePath("logo_alpha.gif"))
630         logowidget = ttk.Label(mainFrame, image=self.logo, borderwidth=0, relief="solid").grid(row=0,column=0, columnspan=5, pady=10)
631
632         optionsRow += 2
633
634         namelbl = ttk.Label(mainFrame, text='Project Name :').grid(row=optionsRow, column=0, sticky=tk.E)
635         self.projectName = tk.StringVar()
636
637         if args.name != None:
638             self.projectName.set(args.name)
639         else:
640             self.projectName.set('ProjectName')
641
642         nameEntry = ttk.Entry(mainFrame, textvariable=self.projectName).grid(row=optionsRow, column=1, sticky=tk.W+tk.E, padx=5)
643
644         optionsRow += 1
645
646         locationlbl = ttk.Label(mainFrame, text='Location :').grid(row=optionsRow, column=0, sticky=tk.E)
647         self.locationName = tk.StringVar()
648         self.locationName.set(os.getcwd())
649         locationEntry = ttk.Entry(mainFrame, textvariable=self.locationName).grid(row=optionsRow, column=1, columnspan=3, sticky=tk.W+tk.E, padx=5)
650         locationBrowse = ttk.Button(mainFrame, text='Browse', command=self.browse).grid(row=3, column=4)
651
652         optionsRow += 1
653
654         ttk.Label(mainFrame, text = "Board Type :").grid(row=optionsRow, column=0, padx=4, sticky=tk.E)
655         self.boardtype = ttk.Combobox(mainFrame, values=boardtype_list, )
656         self.boardtype.grid(row=4, column=1, padx=4, sticky=tk.W+tk.E)
657         self.boardtype.set('pico')
658         self.boardtype.bind('<<ComboboxSelected>>',self.boardtype_change_callback)
659         optionsRow += 1
660
661         # Features section
662         featuresframe = ttk.LabelFrame(mainFrame, text="Library Options", relief=tk.RIDGE, borderwidth=2)
663         featuresframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=5, ipadx=5, padx=5, pady=5, sticky=tk.E+tk.W)
664
665         s = (len(features_list)/3)
666
667         self.feature_checkbox_vars = []
668         row = 0
669         col = 0
670         for i in features_list:
671             var = tk.StringVar(value='') # Off by default for the moment
672             c = features_list[i][GUI_TEXT]
673             cb = ttk.Checkbutton(featuresframe, text = c, var=var, onvalue=i, offvalue='')
674             cb.grid(row=row, column=col, padx=15, pady=2, ipadx=1, ipady=1, sticky=tk.E+tk.W)
675             self.feature_checkbox_vars.append(var)
676             row+=1
677             if row >= s:
678                 col+=1
679                 row = 0
680
681         optionsRow += 5
682
683         # PicoW options section
684         self.picowSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Pico Wireless Options")
685         self.picowSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
686         self.pico_wireless = tk.StringVar()
687
688         col = 0
689         row = 0
690         for i in picow_options_list:
691             rb = ttk.Radiobutton(self.picowSubframe, text=picow_options_list[i][GUI_TEXT], variable=self.pico_wireless, val=i)
692             rb.grid(row=row, column=col,  padx=15, pady=1, sticky=tk.E+tk.W)
693             col+=1
694             if col == 3:
695                 col=0
696                 row+=1
697
698         # DOnt actually need any settings at the moment.
699         # ttk.Button(self.picowSubframe, text='Settings', command=self.wirelessSettings).grid(row=0, column=4, padx=5, pady=2, sticky=tk.E)
700
701         self.setState(self.picowSubframe, "disabled")
702
703         optionsRow += 3
704
705         # output options section
706         ooptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Console Options")
707         ooptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
708
709         self.wantUART = tk.IntVar()
710         self.wantUART.set(args.uart)
711         ttk.Checkbutton(ooptionsSubframe, text="Console over UART", variable=self.wantUART).grid(row=0, column=0, padx=4, sticky=tk.W)
712
713         self.wantUSB = tk.IntVar()
714         self.wantUSB.set(args.usb)
715         ttk.Checkbutton(ooptionsSubframe, text="Console over USB (Disables other USB use)", variable=self.wantUSB).grid(row=0, column=1, padx=4, sticky=tk.W)
716
717         optionsRow += 2
718
719         # Code options section
720         coptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Code Options")
721         coptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=3, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
722
723         self.wantExamples = tk.IntVar()
724         self.wantExamples.set(args.examples)
725         ttk.Checkbutton(coptionsSubframe, text="Add examples for Pico library", variable=self.wantExamples).grid(row=0, column=0, padx=4, sticky=tk.W)
726
727         self.wantRunFromRAM = tk.IntVar()
728         self.wantRunFromRAM.set(args.runFromRAM)
729         ttk.Checkbutton(coptionsSubframe, text="Run from RAM", variable=self.wantRunFromRAM).grid(row=0, column=1, padx=4, sticky=tk.W)
730
731         self.wantCPP = tk.IntVar()
732         self.wantCPP.set(args.cpp)
733         ttk.Checkbutton(coptionsSubframe, text="Generate C++", variable=self.wantCPP).grid(row=0, column=3, padx=4, sticky=tk.W)
734
735         ttk.Button(coptionsSubframe, text="Advanced...", command=self.config).grid(row=0, column=4, sticky=tk.E)
736
737         self.wantCPPExceptions = tk.IntVar()
738         self.wantCPPExceptions.set(args.cppexceptions)
739         ttk.Checkbutton(coptionsSubframe, text="Enable C++ exceptions", variable=self.wantCPPExceptions).grid(row=1, column=0, padx=4, sticky=tk.W)
740
741         self.wantCPPRTTI = tk.IntVar()
742         self.wantCPPRTTI.set(args.cpprtti)
743         ttk.Checkbutton(coptionsSubframe, text="Enable C++ RTTI", variable=self.wantCPPRTTI).grid(row=1, column=1, padx=4, sticky=tk.W)
744
745         optionsRow += 3
746
747         # Build Options section
748
749         boptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Build Options")
750         boptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
751
752         self.wantBuild = tk.IntVar()
753         self.wantBuild.set(args.build)
754         ttk.Checkbutton(boptionsSubframe, text="Run build after generation", variable=self.wantBuild).grid(row=0, column=0, padx=4, sticky=tk.W)
755
756         self.wantOverwrite = tk.IntVar()
757         self.wantOverwrite.set(args.overwrite)
758         ttk.Checkbutton(boptionsSubframe, text="Overwrite existing projects", variable=self.wantOverwrite).grid(row=0, column=1, padx=4, sticky=tk.W)
759
760         optionsRow += 2
761         
762         # IDE Options section
763
764         vscodeoptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="IDE Options")
765         vscodeoptionsSubframe.grid_columnconfigure(2, weight=1)
766         vscodeoptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
767
768         self.wantVSCode = tk.IntVar()
769         if args.project is None:
770             self.wantVSCode.set(False)
771         else:
772             self.wantVSCode.set('vscode' in args.project)
773         ttk.Checkbutton(vscodeoptionsSubframe, text="Create VSCode project", variable=self.wantVSCode).grid(row=0, column=0, padx=4, sticky=tk.W)
774
775         ttk.Label(vscodeoptionsSubframe, text = "     Debugger:").grid(row=0, column=1, padx=4, sticky=tk.W)
776
777         self.debugger = ttk.Combobox(vscodeoptionsSubframe, values=debugger_list, state="readonly")
778         self.debugger.grid(row=0, column=2, padx=4, sticky=tk.EW)
779         self.debugger.current(args.debugger)
780
781         optionsRow += 2
782
783         # OK, Cancel, Help section
784         # creating buttons
785         QuitButton = ttk.Button(mainFrame, text="Quit", command=self.quit).grid(row=optionsRow, column=4, stick=tk.E, padx=10, pady=5)
786         OKButton = ttk.Button(mainFrame, text="OK", command=self.OK).grid(row=optionsRow, column=3, padx=4, pady=5, sticky=tk.E)
787
788         # TODO help not implemented yet
789         # HelpButton = ttk.Button(mainFrame, text="Help", command=self.help).grid(row=optionsRow, column=0, pady=5)
790
791         # You can set a default path here, replace the string with whereever you want.
792         # self.locationName.set('/home/pi/pico_projects')
793
794     def GetFeatures(self):
795         features = []
796
797         i = 0
798         for cb in self.feature_checkbox_vars:
799             s = cb.get()
800             if s != '':
801                 features.append(s)
802
803         picow_extra = self.pico_wireless.get()
804
805         if picow_extra != 'picow_none':
806             features.append(picow_extra)
807
808         return features
809
810     def quit(self):
811         # TODO Check if we want to exit here
812         sys.exit(0)
813
814     def OK(self):
815         # OK, grab all the settings from the page, then call the generators
816         projectPath = self.locationName.get()
817         features = self.GetFeatures()
818         projects = list()
819         if (self.wantVSCode.get()):
820             projects.append("vscode")
821
822         params={
823                 'sdkPath'       : self.sdkpath,
824                 'projectRoot'   : Path(projectPath),
825                 'projectName'   : self.projectName.get(),
826                 'wantGUI'       : True,
827                 'wantOverwrite' : self.wantOverwrite.get(),
828                 'wantBuild'     : self.wantBuild.get(),
829                 'boardtype'     : self.boardtype.get(),
830                 'features'      : features,
831                 'projects'      : projects,
832                 'configs'       : self.configs,
833                 'wantRunFromRAM': self.wantRunFromRAM.get(),
834                 'wantExamples'  : self.wantExamples.get(),
835                 'wantUART'      : self.wantUART.get(),
836                 'wantUSB'       : self.wantUSB.get(),
837                 'wantCPP'       : self.wantCPP.get(),
838                 'debugger'      : self.debugger.current(),
839                 'exceptions'    : self.wantCPPExceptions.get(),
840                 'rtti'          : self.wantCPPRTTI.get(),
841                 'ssid'          : self.ssid,
842                 'password'      : self.password,
843                 }
844
845         DoEverything(self, params)
846
847     def browse(self):
848         name = fd.askdirectory()
849         self.locationName.set(name)
850
851     def help(self):
852         print("Help TODO")
853
854     def config(self):
855         # Run the configuration window
856         self.configs = ConfigurationWindow(self, self.configs).get()
857
858 def CheckPrerequisites():
859     global isMac, isWindows
860     isMac = (platform.system() == 'Darwin')
861     isWindows = (platform.system() == 'Windows')
862
863     # Do we have a compiler?
864     return shutil.which(COMPILER_NAME)
865
866
867 def CheckSDKPath(gui):
868     sdkPath = os.getenv('PICO_SDK_PATH')
869
870     if sdkPath == None:
871         m = 'Unable to locate the Raspberry Pi Pico SDK, PICO_SDK_PATH is not set'
872         if (gui):
873             RunWarning(m)
874         else:
875             print(m)
876     elif not os.path.isdir(sdkPath):
877         m = 'Unable to locate the Raspberry Pi Pico SDK, PICO_SDK_PATH does not point to a directory'
878         if (gui):
879             RunWarning(m)
880         else:
881             print(m)
882         sdkPath = None
883
884     return sdkPath
885
886 def GetFilePath(filename):
887     if os.path.islink(__file__):
888         script_file = os.readlink(__file__)
889     else:
890         script_file = __file__
891     return os.path.join(os.path.dirname(script_file), filename)
892
893 def ParseCommandLine():
894     debugger_flags = ', '.join('{} = {}'.format(i, v) for i, v in enumerate(debugger_list))
895     parser = argparse.ArgumentParser(description='Pico Project generator')
896     parser.add_argument("name", nargs="?", help="Name of the project")
897     parser.add_argument("-t", "--tsv", help="Select an alternative pico_configs.tsv file", default=GetFilePath("pico_configs.tsv"))
898     parser.add_argument("-o", "--output", help="Set an alternative CMakeList.txt filename", default="CMakeLists.txt")
899     parser.add_argument("-x", "--examples", action='store_true', help="Add example code for the Pico standard library")
900     parser.add_argument("-l", "--list", action='store_true', help="List available features")
901     parser.add_argument("-c", "--configs", action='store_true', help="List available project configuration items")
902     parser.add_argument("-f", "--feature", action='append', help="Add feature to generated project")
903     parser.add_argument("-over", "--overwrite", action='store_true', help="Overwrite any existing project AND files")
904     parser.add_argument("-b", "--build", action='store_true', help="Build after project created")
905     parser.add_argument("-g", "--gui", action='store_true', help="Run a GUI version of the project generator")
906     parser.add_argument("-p", "--project", action='append', help="Generate projects files for IDE. Options are: vscode")
907     parser.add_argument("-r", "--runFromRAM", action='store_true', help="Run the program from RAM rather than flash")
908     parser.add_argument("-uart", "--uart", action='store_true', default=1, help="Console output to UART (default)")
909     parser.add_argument("-nouart", "--nouart", action='store_true', default=0, help="Disable console output to UART")
910     parser.add_argument("-usb", "--usb", action='store_true', help="Console output to USB (disables other USB functionality")
911     parser.add_argument("-cpp", "--cpp", action='store_true', default=0, help="Generate C++ code")
912     parser.add_argument("-cpprtti", "--cpprtti", action='store_true', default=0, help="Enable C++ RTTI (Uses more memory)")
913     parser.add_argument("-cppex", "--cppexceptions", action='store_true', default=0, help="Enable C++ exceptions (Uses more memory)")
914     parser.add_argument("-d", "--debugger", type=int, help="Select debugger ({})".format(debugger_flags), default=0)
915     parser.add_argument("-board", "--boardtype", action="store", default='pico', help="Select board type (see --boardlist for available boards)")
916     parser.add_argument("-bl", "--boardlist", action="store_true", help="List available board types")
917     parser.add_argument("-cp", "--cpath", help="Override default VSCode compiler path")
918     parser.add_argument("-root", "--projectRoot", help="Override default project root where the new project will be created")
919
920     return parser.parse_args()
921
922
923 def GenerateMain(folder, projectName, features, cpp):
924
925     if cpp:
926         filename = Path(folder) / (projectName + '.cpp')
927     else:
928         filename = Path(folder) / (projectName + '.c')
929
930     file = open(filename, 'w')
931
932     main = ('#include <stdio.h>\n'
933             '#include "pico/stdlib.h"\n'
934             )
935     file.write(main)
936
937     if (features):
938
939         # Add any includes
940         for feat in features:
941             if (feat in features_list):
942                 o = f'#include "{features_list[feat][H_FILE]}"\n'
943                 file.write(o)
944             if (feat in stdlib_examples_list):
945                 o = f'#include "{stdlib_examples_list[feat][H_FILE]}"\n'
946                 file.write(o)
947             if (feat in picow_options_list):
948                 o = f'#include "{picow_options_list[feat][H_FILE]}"\n'
949                 file.write(o)
950
951         file.write('\n')
952
953         # Add any defines
954         for feat in features:
955             if (feat in code_fragments_per_feature):
956                 for s in code_fragments_per_feature[feat][DEFINES]:
957                     file.write(s)
958                     file.write('\n')
959                 file.write('\n')
960
961     main = ('\n\n'
962             'int main()\n'
963             '{\n'
964             '    stdio_init_all();\n\n'
965             )
966
967     if (features):
968         # Add any initialisers
969         indent = 4
970         for feat in features:
971             if (feat in code_fragments_per_feature):
972                 for s in code_fragments_per_feature[feat][INITIALISERS]:
973                     main += (" " * indent)
974                     main += s
975                     main += '\n'
976             main += '\n'
977
978     main += ('    puts("Hello, world!");\n\n'
979              '    return 0;\n'
980              '}\n'
981             )
982
983     file.write(main)
984
985     file.close()
986
987
988 def GenerateCMake(folder, params):
989    
990     filename = Path(folder) / CMAKELIST_FILENAME
991     projectName = params['projectName']
992     board_type = params['boardtype']
993
994     # OK, for the path, CMake will accept forward slashes on Windows, and thats
995     # seemingly a bit easier to handle than the backslashes
996     p = str(params['sdkPath']).replace('\\','/')
997     sdk_path = f'"{p}"'
998
999     cmake_header1 = (f"# Generated Cmake Pico project file\n\n"
1000                  "cmake_minimum_required(VERSION 3.13)\n\n"
1001                  "set(CMAKE_C_STANDARD 11)\n"
1002                  "set(CMAKE_CXX_STANDARD 17)\n\n"
1003                  "# Initialise pico_sdk from installed location\n"
1004                  "# (note this can come from environment, CMake cache etc)\n"
1005                  f"set(PICO_SDK_PATH {sdk_path})\n\n"
1006                  f"set(PICO_BOARD {board_type} CACHE STRING \"Board type\")\n\n"
1007                  "# Pull in Raspberry Pi Pico SDK (must be before project)\n"
1008                  "include(pico_sdk_import.cmake)\n\n"
1009                  "if (PICO_SDK_VERSION_STRING VERSION_LESS \"1.4.0\")\n"
1010                  "  message(FATAL_ERROR \"Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}\")\n"
1011                  "endif()\n\n"
1012                  f"project({projectName} C CXX ASM)\n"
1013                 )
1014     
1015     cmake_header3 = (
1016                 "\n# Initialise the Raspberry Pi Pico SDK\n"
1017                 "pico_sdk_init()\n\n"
1018                 "# Add executable. Default name is the project name, version 0.1\n\n"
1019                 )
1020
1021
1022     file = open(filename, 'w')
1023
1024     file.write(cmake_header1)
1025
1026     if params['exceptions']:
1027         file.write("\nset(PICO_CXX_ENABLE_EXCEPTIONS 1)\n")
1028
1029     if params['rtti']:
1030         file.write("\nset(PICO_CXX_ENABLE_RTTI 1)\n")
1031
1032     file.write(cmake_header3)
1033
1034     # add the preprocessor defines for overall configuration
1035     if params['configs']:
1036         file.write('# Add any PICO_CONFIG entries specified in the Advanced settings\n')
1037         for c, v in params['configs'].items():
1038             if v == "True":
1039                 v = "1"
1040             elif v == "False":
1041                 v = "0"
1042             file.write(f'add_compile_definitions({c} = {v})\n')
1043         file.write('\n')
1044
1045     # No GUI/command line to set a different executable name at this stage
1046     executableName = projectName
1047
1048     if params['wantCPP']:
1049         file.write(f'add_executable({projectName} {projectName}.cpp )\n\n')
1050     else:
1051         file.write(f'add_executable({projectName} {projectName}.c )\n\n')
1052
1053     file.write(f'pico_set_program_name({projectName} "{executableName}")\n')
1054     file.write(f'pico_set_program_version({projectName} "0.1")\n\n')
1055
1056     if params['wantRunFromRAM']:
1057         file.write(f'# no_flash means the target is to run from RAM\n')
1058         file.write(f'pico_set_binary_type({projectName} no_flash)\n\n')
1059
1060     # Console output destinations
1061     if params['wantUART']:
1062         file.write(f'pico_enable_stdio_uart({projectName} 1)\n')
1063     else:
1064         file.write(f'pico_enable_stdio_uart({projectName} 0)\n')
1065
1066     if params['wantUSB']:
1067         file.write(f'pico_enable_stdio_usb({projectName} 1)\n\n')
1068     else:
1069         file.write(f'pico_enable_stdio_usb({projectName} 0)\n\n')
1070
1071     # If we need wireless, check for SSID and password
1072     # removed for the moment as these settings are currently only needed for the pico-examples
1073     # but may be required in here at a later date.
1074     if False:
1075         if 'ssid' in params or 'password' in params:
1076             file.write('# Add any wireless access point information\n')
1077             file.write(f'target_compile_definitions({projectName} PRIVATE\n')
1078             if 'ssid' in params:
1079                 file.write(f'WIFI_SSID=\" {params["ssid"]} \"\n')
1080             else:
1081                 file.write(f'WIFI_SSID=\"${WIFI_SSID}\"')
1082
1083             if 'password' in params:
1084                 file.write(f'WIFI_PASSWORD=\"{params["password"]}\"\n')
1085             else:
1086                 file.write(f'WIFI_PASSWORD=\"${WIFI_PASSWORD}\"')
1087             file.write(')\n\n')
1088
1089     # Standard libraries
1090     file.write('# Add the standard library to the build\n')
1091     file.write(f'target_link_libraries({projectName}\n')
1092     file.write("        " + STANDARD_LIBRARIES)
1093     file.write(')\n\n')
1094
1095     # Standard include directories
1096     file.write('# Add the standard include files to the build\n')
1097     file.write(f'target_include_directories({projectName} PRIVATE\n')
1098     file.write("  ${CMAKE_CURRENT_LIST_DIR}\n")
1099     file.write("  ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required\n")
1100     file.write(')\n\n')
1101
1102     # Selected libraries/features
1103     if (params['features']):
1104         file.write('# Add any user requested libraries\n')
1105         file.write(f'target_link_libraries({projectName} \n')
1106         for feat in params['features']:
1107             if (feat in features_list):
1108                 file.write("        " + features_list[feat][LIB_NAME] + '\n')
1109             if (feat in picow_options_list):
1110                 file.write("        " + picow_options_list[feat][LIB_NAME] + '\n')
1111         file.write('        )\n\n')
1112
1113     file.write(f'pico_add_extra_outputs({projectName})\n\n')
1114
1115     file.close()
1116
1117
1118 # Generates the requested project files, if any
1119 def generateProjectFiles(projectPath, projectName, sdkPath, projects, debugger):
1120
1121     oldCWD = os.getcwd()
1122
1123     os.chdir(projectPath)
1124
1125     debugger = debugger_config_list[debugger]
1126     gdbPath =  "arm-none-eabi-gdb" if isWindows else "gdb-multiarch"
1127     # Need to escape windows files paths backslashes
1128     cPath = str(compilerPath).replace('\\', '\\\\' )
1129
1130     for p in projects :
1131         if p == 'vscode':
1132             launch = ('{\n'
1133                   '  "version": "0.2.0",\n'
1134                   '  "configurations": [\n'
1135                   '    {\n'
1136                   '      "name": "Pico Debug (Cortex-Debug)",\n'
1137                   '      "cwd": "${workspaceRoot}",\n'
1138                   '      "executable": "${command:cmake.launchTargetPath}",\n'
1139                   '      "request": "launch",\n'
1140                   '      "type": "cortex-debug",\n'
1141                   '      "servertype": "openocd",\n'
1142                   f'      "gdbPath": "{gdbPath}",\n'
1143                   '      "device": "RP2040",\n'
1144                   '      "configFiles": [\n'
1145                   f'        "interface/{debugger}",\n'
1146                   '        "target/rp2040.cfg"\n'
1147                   '        ],\n'
1148                   '      "svdFile": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd",\n'
1149                   '      "runToEntryPoint": "main",\n'
1150                   '      // Give restart the same functionality as runToEntryPoint - main\n'
1151                   '      "postRestartCommands": [\n'
1152                   '          "break main",\n'
1153                   '          "continue"\n'
1154                   '      ],\n'
1155                   '      "openOCDLaunchCommands": [\n'
1156                   '          "adapter speed 1000"\n'
1157                   '      ]\n'
1158                   '    },\n'
1159                   '    {\n'
1160                   '      "name": "Pico Debug (Cortex-Debug with external OpenOCD)",\n'
1161                   '      "cwd": "${workspaceRoot}",\n'
1162                   '      "executable": "${command:cmake.launchTargetPath}",\n'
1163                   '      "request": "launch",\n'
1164                   '      "type": "cortex-debug",\n'
1165                   '      "servertype": "external",\n'
1166                   '      "gdbTarget": "localhost:3333",\n'
1167                   f'      "gdbPath": "{gdbPath}",\n'
1168                   '      "device": "RP2040",\n'
1169                   '      "svdFile": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd",\n'
1170                   '      "runToEntryPoint": "main",\n'
1171                   '      // Give restart the same functionality as runToEntryPoint - main\n'
1172                   '      "postRestartCommands": [\n'
1173                   '          "break main",\n'
1174                   '          "continue"\n'
1175                   '      ]\n'
1176                   '    },\n'
1177                   '    {\n'
1178                   '      "name": "Pico Debug (C++ Debugger)",\n'
1179                   '      "type": "cppdbg",\n'
1180                   '      "request": "launch",\n'
1181                   '      "cwd": "${workspaceRoot}",\n'
1182                   '      "program": "${command:cmake.launchTargetPath}",\n'
1183                   '      "MIMode": "gdb",\n'
1184                   '      "miDebuggerPath": "{gdbPath}",\n'
1185                   '      "miDebuggerServerAddress": "localhost:3333",\n'
1186                   '      "debugServerPath": "openocd",\n'
1187                   '      "debugServerArgs": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \\"adapter speed 1000\\"",\n'
1188                   '      "serverStarted": "Listening on port .* for gdb connections",\n'
1189                   '      "filterStderr": true,\n'
1190                   '      "stopAtEntry": true,\n'
1191                   '      "hardwareBreakpoints": {\n'
1192                   '        "require": true,\n'
1193                   '        "limit": 4\n'
1194                   '      },\n'
1195                   '      "preLaunchTask": "Flash",\n'
1196                   '      "svdPath": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd"\n'
1197                   '    },\n'
1198                   '  ]\n'
1199                   '}\n')
1200
1201             properties = ('{\n'
1202                   '  "configurations": [\n'
1203                   '    {\n'
1204                   '      "name": "Pico",\n'
1205                   '      "includePath": [\n'
1206                   '        "${workspaceFolder}/**",\n'
1207                   '        "${env:PICO_SDK_PATH}/**"\n'
1208                   '      ],\n'
1209                   '      "defines": [],\n'
1210                   f'      "compilerPath": "{cPath}",\n'
1211                   '      "cStandard": "c17",\n'
1212                   '      "cppStandard": "c++14",\n'
1213                   '      "intelliSenseMode": "linux-gcc-arm",\n'
1214                   '      "configurationProvider" : "ms-vscode.cmake-tools"\n'
1215                   '    }\n'
1216                   '  ],\n'
1217                   '  "version": 4\n'
1218                   '}\n')
1219
1220             settings = ( '{\n'
1221                    '  "cmake.statusbar.advanced": {\n'
1222                    '    "debug": {\n'
1223                    '      "visibility": "hidden"\n'
1224                    '    },\n'
1225                    '    "launch": {\n'
1226                    '      "visibility": "hidden"\n'
1227                    '    },\n'
1228                    '    "build": {\n'
1229                    '      "visibility": "hidden"\n'
1230                    '    },\n'
1231                    '    "buildTarget": {\n'
1232                    '      "visibility": "hidden"\n'
1233                    '    }\n'
1234                    '  },\n'
1235                    '  "cmake.buildBeforeRun": true,\n'
1236                    '  "cmake.configureOnOpen": true,\n'
1237                    '  "cmake.configureSettings": {\n'
1238                    '    "CMAKE_MODULE_PATH": "${env:PICO_INSTALL_PATH}/pico-sdk-tools"\n'
1239                    '  },\n'
1240                    '  "cmake.generator": "Ninja",\n'
1241                    '  "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"\n'
1242                    '}\n')
1243
1244             extensions = ( '{\n'
1245                    '  "recommendations": [\n'
1246                    '    "marus25.cortex-debug",\n'
1247                    '    "ms-vscode.cmake-tools",\n'
1248                    '    "ms-vscode.cpptools",\n'
1249                    '    "ms-vscode.cpptools-extension-pack",\n'
1250                    '    "ms-vscode.vscode-serial-monitor"\n'
1251                    '  ]\n'
1252                    '}\n')
1253
1254             # Create a build folder, and run our cmake project build from it
1255             if not os.path.exists(VSCODE_FOLDER):
1256                 os.mkdir(VSCODE_FOLDER)
1257
1258             os.chdir(VSCODE_FOLDER)
1259
1260             filename = VSCODE_LAUNCH_FILENAME
1261             file = open(filename, 'w')
1262             file.write(launch)
1263             file.close()
1264
1265             file = open(VSCODE_C_PROPERTIES_FILENAME, 'w')
1266             file.write(properties)
1267             file.close()
1268
1269             file = open(VSCODE_SETTINGS_FILENAME, 'w')
1270             file.write(settings)
1271             file.close()
1272
1273             file = open(VSCODE_EXTENSIONS_FILENAME, 'w')
1274             file.write(extensions)
1275             file.close()
1276
1277         else :
1278             print('Unknown project type requested')
1279
1280     os.chdir(oldCWD)
1281
1282
1283 def LoadConfigurations():
1284     try:
1285         with open(args.tsv) as tsvfile:
1286             reader = csv.DictReader(tsvfile, dialect='excel-tab')
1287             for row in reader:
1288                 configuration_dictionary.append(row)
1289     except:
1290         print("No Pico configurations file found. Continuing without")
1291
1292 def LoadBoardTypes(sdkPath):
1293     # Scan the boards folder for all header files, extract filenames, and make a list of the results
1294     # default folder is <PICO_SDK_PATH>/src/boards/include/boards/*
1295     # If the PICO_BOARD_HEADER_DIRS environment variable is set, use that as well
1296
1297     loc = sdkPath / "src/boards/include/boards"
1298     boards=[]
1299     for x in Path(loc).iterdir():
1300         if x.suffix == '.h':
1301             boards.append(x.stem)
1302
1303     loc = os.getenv('PICO_BOARD_HEADER_DIRS')
1304
1305     if loc != None:
1306         for x in Path(loc).iterdir():
1307             if x.suffix == '.h':
1308                 boards.append(x.stem)
1309
1310     return boards
1311
1312 def DoEverything(parent, params):
1313
1314     if not os.path.exists(params['projectRoot']):
1315         if params['wantGUI']:
1316             mb.showerror('Raspberry Pi Pico Project Generator', 'Invalid project path. Select a valid path and try again')
1317             return
1318         else:
1319             print('Invalid project path')
1320             sys.exit(-1)
1321
1322     oldCWD = os.getcwd()
1323     os.chdir(params['projectRoot'])
1324
1325     # Create our project folder as subfolder
1326     os.makedirs(params['projectName'], exist_ok=True)
1327
1328     os.chdir(params['projectName'])
1329
1330     projectPath = params['projectRoot'] / params['projectName']
1331
1332     # First check if there is already a project in the folder
1333     # If there is we abort unless the overwrite flag it set
1334     if os.path.exists(CMAKELIST_FILENAME):
1335         if not params['wantOverwrite'] :
1336             if params['wantGUI']:
1337                 # We can ask the user if they want to overwrite
1338                 y = mb.askquestion('Raspberry Pi Pico Project Generator', 'There already appears to be a project in this folder. \nPress Yes to overwrite project files, or Cancel to chose another folder')
1339                 if y != 'yes':
1340                     return
1341             else:
1342                 print('There already appears to be a project in this folder. Use the --overwrite option to overwrite the existing project')
1343                 sys.exit(-1)
1344
1345         # We should really confirm the user wants to overwrite
1346         #print('Are you sure you want to overwrite the existing project files? (y/N)')
1347         #c = input().split(" ")[0]
1348         #if c != 'y' and c != 'Y' :
1349         #    sys.exit(0)
1350
1351     # Copy the SDK finder cmake file to our project folder
1352     # Can be found here <PICO_SDK_PATH>/external/pico_sdk_import.cmake
1353     shutil.copyfile(params['sdkPath'] / 'external' / 'pico_sdk_import.cmake', projectPath / 'pico_sdk_import.cmake' )
1354
1355     if params['features']:
1356         features_and_examples = params['features'][:]
1357     else:
1358         features_and_examples= []
1359
1360     if params['wantExamples']:
1361         features_and_examples = list(stdlib_examples_list.keys()) + features_and_examples
1362
1363     GenerateMain('.', params['projectName'], features_and_examples, params['wantCPP'])
1364
1365     GenerateCMake('.', params)
1366
1367     # If we have any ancilliary files, copy them to our project folder
1368     # Currently only the picow with lwIP support needs an extra file, so just check that list
1369     for feat in features_and_examples:
1370         if feat in picow_options_list:
1371             if picow_options_list[feat][ANCILLARY_FILE] != "":
1372                 shutil.copy(sourcefolder + "/" + picow_options_list[feat][ANCILLARY_FILE], projectPath / picow_options_list[feat][ANCILLARY_FILE])
1373
1374     # Create a build folder, and run our cmake project build from it
1375     if not os.path.exists('build'):
1376         os.mkdir('build')
1377
1378     os.chdir('build')
1379
1380     # If we are overwriting a previous project, we should probably clear the folder, but that might delete something the users thinks is important, so
1381     # for the moment, just delete the CMakeCache.txt file as certain changes may need that to be recreated.
1382
1383     if os.path.exists(CMAKECACHE_FILENAME):
1384         os.remove(CMAKECACHE_FILENAME)
1385
1386     cpus = os.cpu_count()
1387     if cpus == None:
1388         cpus = 1
1389
1390     if isWindows:
1391         # Had a special case report, when using MinGW, need to check if using nmake or mingw32-make.
1392         if shutil.which("mingw32-make"):
1393             # Assume MinGW environment
1394             cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G "MinGW Makefiles" ..'
1395             makeCmd = 'mingw32-make '
1396         elif shutil.which("ninja"):
1397             # When installing SDK version 1.5.0 on windows with installer pico-setup-windows-x64-standalone.exe, ninja is used 
1398             cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja ..'
1399             makeCmd = 'ninja '        
1400         else:
1401             # Everything else assume nmake
1402             cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G "NMake Makefiles" ..'
1403             makeCmd = 'nmake '
1404     else:
1405         # Ninja now works OK under Linux, so if installed use it by default. It's faster.
1406         if shutil.which("ninja"):
1407             cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja ..'
1408             makeCmd = 'ninja '
1409         else:
1410             cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug ..'
1411             makeCmd = 'make -j' + str(cpus)
1412
1413     if params['wantGUI']:
1414         RunCommandInWindow(parent, cmakeCmd)
1415     else:
1416         os.system(cmakeCmd)
1417
1418     if params['projects']:
1419         generateProjectFiles(projectPath, params['projectName'], params['sdkPath'], params['projects'], params['debugger'])
1420
1421     if params['wantBuild']:
1422         if params['wantGUI']:
1423             RunCommandInWindow(parent, makeCmd)
1424         else:
1425             os.system(makeCmd)
1426             print('\nIf the application has built correctly, you can now transfer it to the Raspberry Pi Pico board')
1427
1428     os.chdir(oldCWD)
1429
1430
1431 ###################################################################################
1432 # main execution starteth here
1433
1434 sourcefolder = os.path.dirname(os.path.abspath(__file__))
1435
1436 args = ParseCommandLine()
1437
1438 if args.nouart:
1439     args.uart = False
1440
1441 if args.debugger > len(debugger_list) - 1:
1442     args.debugger = 0
1443
1444 # Check we have everything we need to compile etc
1445 c = CheckPrerequisites()
1446
1447 ## TODO Do both warnings in the same error message so user does have to keep coming back to find still more to do
1448
1449 if c == None:
1450     m = f'Unable to find the `{COMPILER_NAME}` compiler\n'
1451     m +='You will need to install an appropriate compiler to build a Raspberry Pi Pico project\n'
1452     m += 'See the Raspberry Pi Pico documentation for how to do this on your particular platform\n'
1453
1454     if (args.gui):
1455         RunWarning(m)
1456     else:
1457         print(m)
1458     sys.exit(-1)
1459
1460 if args.name == None and not args.gui and not args.list and not args.configs and not args.boardlist:
1461     print("No project name specfied\n")
1462     sys.exit(-1)
1463
1464 # Check if we were provided a compiler path, and override the default if so
1465 if args.cpath:
1466     compilerPath = Path(args.cpath)
1467 else:
1468     compilerPath = Path(c)
1469
1470 # load/parse any configuration dictionary we may have
1471 LoadConfigurations()
1472
1473 p = CheckSDKPath(args.gui)
1474
1475 if p == None:
1476     sys.exit(-1)
1477
1478 sdkPath = Path(p)
1479
1480 boardtype_list = LoadBoardTypes(sdkPath)
1481 boardtype_list.sort()
1482
1483 if args.gui:
1484     RunGUI(sdkPath, args) # does not return, only exits
1485
1486 projectRoot = Path(os.getcwd()) if not args.projectRoot else Path(args.projectRoot)
1487
1488 if args.list or args.configs or args.boardlist:
1489     if args.list:
1490         print("Available project features:\n")
1491         for feat in features_list:
1492             print(feat.ljust(6), '\t', features_list[feat][GUI_TEXT])
1493         print('\n')
1494
1495     if args.configs:
1496         print("Available project configuration items:\n")
1497         for conf in configuration_dictionary:
1498             print(conf['name'].ljust(40), '\t', conf['description'])
1499         print('\n')
1500
1501     if args.boardlist:
1502         print("Available board types:\n")
1503         for board in boardtype_list:
1504             print(board)
1505         print('\n')
1506
1507     sys.exit(0)
1508 else :
1509     params={
1510         'sdkPath'       : sdkPath,
1511         'projectRoot'   : projectRoot,
1512         'projectName'   : args.name,
1513         'wantGUI'       : False,
1514         'wantOverwrite' : args.overwrite,
1515         'boardtype'     : args.boardtype,
1516         'wantBuild'     : args.build,
1517         'features'      : args.feature,
1518         'projects'      : args.project,
1519         'configs'       : (),
1520         'wantRunFromRAM': args.runFromRAM,
1521         'wantExamples'  : args.examples,
1522         'wantUART'      : args.uart,
1523         'wantUSB'       : args.usb,
1524         'wantCPP'       : args.cpp,
1525         'debugger'      : args.debugger,
1526         'exceptions'    : args.cppexceptions,
1527         'rtti'          : args.cpprtti,
1528         'ssid'          : '',
1529         'password'      : '',
1530         }
1531
1532     DoEverything(None, params)
This page took 0.125084 seconds and 4 git commands to generate.