4 # Copyright (c) 2020-2023 Raspberry Pi (Trading) Ltd.
6 # SPDX-License-Identifier: BSD-3-Clause
12 from pyexpat import features
14 from pathlib import Path
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
28 CMAKELIST_FILENAME = 'CMakeLists.txt'
29 CMAKECACHE_FILENAME = 'CMakeCache.txt'
31 COMPILER_NAME = 'arm-none-eabi-gcc'
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_TASKS_FILENAME ='tasks.json'
38 VSCODE_FOLDER='.vscode'
40 CONFIG_UNSET="Not set"
42 # Standard libraries for all builds
43 # And any more to string below, space separator
44 STANDARD_LIBRARIES = 'pico_stdlib'
46 # Indexed on feature name, tuple contains the C file, the H file and the CMake project name for the feature.
47 # Some lists may contain an extra/ancillary file needed for that feature
55 'spi' : ("SPI", "spi.c", "hardware/spi.h", "hardware_spi"),
56 'i2c' : ("I2C interface", "i2c.c", "hardware/i2c.h", "hardware_i2c"),
57 'dma' : ("DMA support", "dma.c", "hardware/dma.h", "hardware_dma"),
58 'pio' : ("PIO interface", "pio.c", "hardware/pio.h", "hardware_pio"),
59 'interp' : ("HW interpolation", "interp.c", "hardware/interp.h", "hardware_interp"),
60 'timer' : ("HW timer", "timer.c", "hardware/timer.h", "hardware_timer"),
61 'watchdog' : ("HW watchdog", "watch.c", "hardware/watchdog.h", "hardware_watchdog"),
62 'clocks' : ("HW clocks", "clocks.c", "hardware/clocks.h", "hardware_clocks"),
65 picow_options_list = {
66 'picow_none' : ("None", "", "", "", ""),
67 'picow_led' : ("PicoW onboard LED", "", "pico/cyw43_arch.h", "pico_cyw43_arch_none", ""),
68 'picow_poll' : ("Polled lwIP", "", "pico/cyw43_arch.h", "pico_cyw43_arch_lwip_poll", "lwipopts.h"),
69 'picow_background' :("Background lwIP", "", "pico/cyw43_arch.h", "pico_cyw43_arch_lwip_threadsafe_background", "lwipopts.h"),
70 # 'picow_freertos' : ("Full lwIP (FreeRTOS)", "", "pico/cyw43_arch.h", "pico_cyw43_arch_lwip_sys_freertos", "lwipopts.h"),
73 stdlib_examples_list = {
74 'uart': ("UART", "uart.c", "hardware/uart.h", "hardware_uart"),
75 'gpio' : ("GPIO interface", "gpio.c", "hardware/gpio.h", "hardware_gpio"),
76 'div' : ("Low level HW Divider", "divider.c", "hardware/divider.h", "hardware_divider")
79 debugger_list = ["DebugProbe (CMSIS-DAP)", "SWD (Pi host)"]
80 debugger_config_list = ["cmsis-dap.cfg", "raspberrypi-swd.cfg"]
84 # Could add an extra item that shows how to use some of the available functions for the feature
87 # This also contains example code for the standard library (see stdlib_examples_list)
88 code_fragments_per_feature = {
91 "// By default the stdout UART is `uart0`, so we will use the second one",
92 "#define UART_ID uart1",
93 "#define BAUD_RATE 9600", "",
94 "// Use pins 4 and 5 for UART1",
95 "// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
96 "#define UART_TX_PIN 4",
97 "#define UART_RX_PIN 5" ),
99 ( "// Set up our UART",
100 "uart_init(UART_ID, BAUD_RATE);",
101 "// Set the TX and RX pins by using the function select on the GPIO",
102 "// Set datasheet for more information on function select",
103 "gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);",
104 "gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);", "" )
108 "// We are going to use SPI 0, and allocate it to the following GPIO pins",
109 "// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
110 "#define SPI_PORT spi0",
111 "#define PIN_MISO 16",
113 "#define PIN_SCK 18",
114 "#define PIN_MOSI 19" ),
116 ( "// SPI initialisation. This example will use SPI at 1MHz.",
117 "spi_init(SPI_PORT, 1000*1000);",
118 "gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);",
119 "gpio_set_function(PIN_CS, GPIO_FUNC_SIO);",
120 "gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);",
121 "gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);", "",
122 "// Chip select is active-low, so we'll initialise it to a driven-high state",
123 "gpio_set_dir(PIN_CS, GPIO_OUT);",
124 "gpio_put(PIN_CS, 1);", "")
129 "// This example will use I2C0 on GPIO8 (SDA) and GPIO9 (SCL) running at 400KHz.",
130 "// Pins can be changed, see the GPIO function select table in the datasheet for information on GPIO assignments",
131 "#define I2C_PORT i2c0",
136 "// I2C Initialisation. Using it at 400Khz.",
137 "i2c_init(I2C_PORT, 400*1000);","",
138 "gpio_set_function(I2C_SDA, GPIO_FUNC_I2C);",
139 "gpio_set_function(I2C_SCL, GPIO_FUNC_I2C);",
140 "gpio_pull_up(I2C_SDA);",
141 "gpio_pull_up(I2C_SCL);"
147 "// Example uses GPIO 2",
151 "// GPIO initialisation.",
152 "// We will make this GPIO an input, and pull it up by default",
154 "gpio_set_dir(GPIO, GPIO_IN);",
155 "gpio_pull_up(GPIO);","",
161 "// Interpolator example code",
162 "interp_config cfg = interp_default_config();",
163 "// Now use the various interpolator library functions for your use case",
164 "// e.g. interp_config_clamp(&cfg, true);",
165 "// interp_config_shift(&cfg, 2);",
166 "// Then set the config ",
167 "interp_set_config(interp0, 0, &cfg);",
173 "int64_t alarm_callback(alarm_id_t id, void *user_data) {",
174 " // Put your timeout handler code in here",
179 "// Timer example code - This example fires off the callback after 2000ms",
180 "add_alarm_in_ms(2000, alarm_callback, NULL, false);"
186 "// Watchdog example code",
187 "if (watchdog_caused_reboot()) {",
188 " // Whatever action you may take if a watchdog caused a reboot",
190 "// Enable the watchdog, requiring the watchdog to be updated every 100ms or the chip will reboot",
191 "// second arg is pause on debug which means the watchdog will pause when stepping through code",
192 "watchdog_enable(100, 1);","",
193 "// You need to call this function at least more often than the 100ms in the enable call to prevent a reboot"
194 "watchdog_update();",
200 "// Example of using the HW divider. The pico_divider library provides a more user friendly set of APIs ",
201 "// over the divider (and support for 64 bit divides), and of course by default regular C language integer",
202 "// divisions are redirected thru that library, meaning you can just use C level `/` and `%` operators and",
203 "// gain the benefits of the fast hardware divider.",
204 "int32_t dividend = 123456;",
205 "int32_t divisor = -321;",
206 "// This is the recommended signed fast divider for general use.",
207 "divmod_result_t result = hw_divider_divmod_s32(dividend, divisor);",
208 "printf(\"%d/%d = %d remainder %d\\n\", dividend, divisor, to_quotient_s32(result), to_remainder_s32(result));",
209 "// This is the recommended unsigned fast divider for general use.",
210 "int32_t udividend = 123456;",
211 "int32_t udivisor = 321;",
212 "divmod_result_t uresult = hw_divider_divmod_u32(udividend, udivisor);",
213 "printf(\"%d/%d = %d remainder %d\\n\", udividend, udivisor, to_quotient_u32(uresult), to_remainder_u32(uresult));"
218 configuration_dictionary = list(dict())
222 compilerPath = Path("/usr/bin/arm-none-eabi-gcc")
227 def GetButtonBackground():
233 def GetButtonTextColour():
236 def RunGUI(sdkpath, args):
238 style = ttk.Style(root)
239 style.theme_use('default')
241 style.configure("TButton", padding=6, relief="groove", border=2, foreground=GetButtonTextColour(), background=GetButtonBackground())
242 style.configure("TLabel", foreground=GetTextColour(), background=GetBackground() )
243 style.configure("TCheckbutton", foreground=GetTextColour(), background=GetBackground())
244 style.configure("TRadiobutton", foreground=GetTextColour(), background=GetBackground() )
245 style.configure("TLabelframe", foreground=GetTextColour(), background=GetBackground() )
246 style.configure("TLabelframe.Label", foreground=GetTextColour(), background=GetBackground() )
247 style.configure("TCombobox", foreground=GetTextColour(), background=GetBackground() )
248 style.configure("TListbox", foreground=GetTextColour(), background=GetBackground() )
250 style.map("TCheckbutton", background = [('disabled', GetBackground())])
251 style.map("TRadiobutton", background = [('disabled', GetBackground())])
252 style.map("TButton", background = [('disabled', GetBackground())])
253 style.map("TLabel", background = [('background', GetBackground())])
254 style.map("TComboBox", background = [('readonly', GetBackground())])
256 app = ProjectWindow(root, sdkpath, args)
258 app.configure(background=GetBackground())
263 def RunWarning(message):
264 mb.showwarning('Raspberry Pi Pico Project Generator', message)
269 def thread_function(text, command, ok):
270 l = shlex.split(command)
271 proc = subprocess.Popen(l, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
272 for line in iter(proc.stdout.readline,''):
275 ok["state"] = tk.NORMAL
277 text.insert(tk.END, line)
280 # Function to run an OS command and display the output in a new modal window
281 class DisplayWindow(tk.Toplevel):
282 def __init__(self, parent, title):
283 tk.Toplevel.__init__(self, parent)
285 self.init_window(title)
287 def init_window(self, title):
290 frame = tk.Frame(self, borderwidth=5, relief=tk.RIDGE)
291 frame.pack(fill=tk.X, expand=True, side=tk.TOP)
293 scrollbar = tk.Scrollbar(frame)
294 self.text = tk.Text(frame, bg='gray14', fg='gray99')
295 scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
296 self.text.pack(side=tk.LEFT, fill=tk.Y)
297 scrollbar.config(command=self.text.yview)
298 self.text.config(yscrollcommand=scrollbar.set)
300 frame1 = tk.Frame(self, borderwidth=1)
301 frame1.pack(fill=tk.X, expand=True, side=tk.BOTTOM)
302 self.OKButton = ttk.Button(frame1, text="OK", command=self.OK)
303 self.OKButton["state"] = tk.DISABLED
307 self.transient(self.parent)
314 def RunCommandInWindow(parent, command):
315 w = DisplayWindow(parent, command)
316 x = threading.Thread(target=thread_function, args=(w.text, command, w.OKButton))
318 parent.wait_window(w)
320 class EditBoolWindow(sd.Dialog):
322 def __init__(self, parent, configitem, current):
324 self.config_item = configitem
325 self.current = current
326 sd.Dialog.__init__(self, parent, "Edit boolean configuration")
329 def body(self, master):
330 self.configure(background=GetBackground())
331 ttk.Label(self, text=self.config_item['name']).pack()
332 self.result = tk.StringVar()
333 self.result.set(self.current)
334 ttk.Radiobutton(master, text="True", variable=self.result, value="True").pack(anchor=tk.W)
335 ttk.Radiobutton(master, text="False", variable=self.result, value="False").pack(anchor=tk.W)
336 ttk.Radiobutton(master, text=CONFIG_UNSET, variable=self.result, value=CONFIG_UNSET).pack(anchor=tk.W)
339 return self.result.get()
341 class EditIntWindow(sd.Dialog):
343 def __init__(self, parent, configitem, current):
345 self.config_item = configitem
346 self.current = current
347 sd.Dialog.__init__(self, parent, "Edit integer configuration")
349 def body(self, master):
350 self.configure(background=GetBackground())
351 str = self.config_item['name'] + " Max = " + self.config_item['max'] + " Min = " + self.config_item['min']
352 ttk.Label(self, text=str).pack()
353 self.input = tk.Entry(self)
354 self.input.pack(pady=4)
355 self.input.insert(0, self.current)
356 ttk.Button(self, text=CONFIG_UNSET, command=self.unset).pack(pady=5)
359 self.result = self.input.get()
360 # Check for numeric entry
364 self.result = CONFIG_UNSET
370 class EditEnumWindow(sd.Dialog):
371 def __init__(self, parent, configitem, current):
373 self.config_item = configitem
374 self.current = current
375 sd.Dialog.__init__(self, parent, "Edit Enumeration configuration")
377 def body(self, master):
378 #self.configure(background=GetBackground())
379 values = self.config_item['enumvalues'].split('|')
380 values.insert(0,'Not set')
381 self.input = ttk.Combobox(self, values=values, state='readonly')
382 self.input.set(self.current)
383 self.input.pack(pady=12)
386 self.result = self.input.get()
393 class ConfigurationWindow(tk.Toplevel):
395 def __init__(self, parent, initial_config):
396 tk.Toplevel.__init__(self, parent)
398 self.results = initial_config
399 self.init_window(self)
401 def init_window(self, args):
402 self.configure(background=GetBackground())
403 self.title("Advanced Configuration")
404 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)
405 ttk.Label(self, text="Name").grid(row=1, column=0, sticky=tk.W)
406 ttk.Label(self, text="Type").grid(row=1, column=1, sticky=tk.W)
407 ttk.Label(self, text="Min").grid(row=1, column=2, sticky=tk.W)
408 ttk.Label(self, text="Max").grid(row=1, column=3, sticky=tk.W)
409 ttk.Label(self, text="Default").grid(row=1, column=4, sticky=tk.W)
410 ttk.Label(self, text="User").grid(row=1, column=5, sticky=tk.W)
412 okButton = ttk.Button(self, text="OK", command=self.ok)
413 cancelButton = ttk.Button(self, text="Cancel", command=self.cancel)
415 self.namelist = tk.Listbox(self, selectmode=tk.SINGLE)
416 self.typelist = tk.Listbox(self, selectmode=tk.SINGLE)
417 self.minlist = tk.Listbox(self, selectmode=tk.SINGLE)
418 self.maxlist = tk.Listbox(self, selectmode=tk.SINGLE)
419 self.defaultlist = tk.Listbox(self, selectmode=tk.SINGLE)
420 self.valuelist = tk.Listbox(self, selectmode=tk.SINGLE)
422 self.descriptionText = tk.Text(self, state=tk.DISABLED, height=2)
424 ## Make a list of our list boxes to make it all easier to handle
425 self.listlist = [self.namelist, self.typelist, self.minlist, self.maxlist, self.defaultlist, self.valuelist]
427 scroll = tk.Scrollbar(self, orient=tk.VERTICAL, command=self.yview)
429 for box in self.listlist:
431 box.config(yscrollcommand=scroll.set)
432 box.bind("<MouseWheel>", self.mousewheel)
433 box.bind("<Button-4>", self.mousewheel)
434 box.bind("<Button-5>", self.mousewheel)
435 box.bind("<<ListboxSelect>>", self.changeSelection)
436 box.bind("<Double-Button>", self.doubleClick)
437 box.config(exportselection=False)
438 box.bind("<Down>", self.OnEntryUpDown)
439 box.bind("<Up>", self.OnEntryUpDown)
441 scroll.grid(column=7, sticky=tk.N + tk.S)
444 for box in self.listlist:
445 box.grid(row=2, column=i, padx=0, sticky=tk.W + tk.E)
448 self.descriptionText.grid(row = 3, column=0, columnspan=4, sticky=tk.W + tk.E)
449 cancelButton.grid(column=5, row = 3, padx=5)
450 okButton.grid(column=4, row = 3, sticky=tk.E, padx=5)
452 # populate the list box with our config options
453 for conf in configuration_dictionary:
454 self.namelist.insert(tk.END, conf['name'])
458 self.typelist.insert(tk.END, s)
459 self.maxlist.insert(tk.END, conf['max'])
460 self.minlist.insert(tk.END, conf['min'])
461 self.defaultlist.insert(tk.END, conf['default'])
463 # see if this config has a setting, our results member has this predefined from init
464 val = self.results.get(conf['name'], CONFIG_UNSET)
465 self.valuelist.insert(tk.END, val)
466 if val != CONFIG_UNSET:
467 self.valuelist.itemconfig(self.valuelist.size() - 1, {'bg':'green'})
469 def yview(self, *args):
470 for box in self.listlist:
473 def mousewheel(self, event):
474 if (event.num == 4): # Linux encodes wheel as 'buttons' 4 and 5
476 elif (event.num == 5):
478 else: # Windows & OSX
481 for box in self.listlist:
482 box.yview("scroll", delta, "units")
485 def changeSelection(self, evt):
487 sellist = box.curselection()
490 index = int(sellist[0])
491 config = self.namelist.get(index)
492 # Now find the description for that config in the dictionary
493 for conf in configuration_dictionary:
494 if conf['name'] == config:
495 self.descriptionText.config(state=tk.NORMAL)
496 self.descriptionText.delete(1.0,tk.END)
497 str = config + "\n" + conf['description']
498 self.descriptionText.insert(1.0, str)
499 self.descriptionText.config(state=tk.DISABLED)
501 # Set all the other list boxes to the same index
502 for b in self.listlist:
504 b.selection_clear(0, tk.END)
505 b.selection_set(index)
507 def OnEntryUpDown(self, event):
509 selection = box.curselection()
512 index = int(selection[0])
513 if event.keysym == 'Up':
515 elif event.keysym == 'Down':
518 if 0 <= index < box.size():
519 for b in self.listlist:
520 b.selection_clear(0, tk.END)
521 b.selection_set(index)
524 def doubleClick(self, evt):
526 index = int(box.curselection()[0])
527 config = self.namelist.get(index)
528 # Get the associated dict entry from our list of configs
529 for conf in configuration_dictionary:
530 if conf['name'] == config:
531 if (conf['type'] == 'bool'):
532 result = EditBoolWindow(self, conf, self.valuelist.get(index)).get()
533 elif (conf['type'] == 'int' or conf['type'] == ""): # "" defaults to int
534 result = EditIntWindow(self, conf, self.valuelist.get(index)).get()
535 elif conf['type'] == 'enum':
536 result = EditEnumWindow(self, conf, self.valuelist.get(index)).get()
538 # Update the valuelist with our new item
539 self.valuelist.delete(index)
540 self.valuelist.insert(index, result)
541 if result != CONFIG_UNSET:
542 self.valuelist.itemconfig(index, {'bg':'green'})
546 # Get the selections, and create a list of them
547 for i, val in enumerate(self.valuelist.get(0, tk.END)):
548 if val != CONFIG_UNSET:
549 self.results[self.namelist.get(i)] = val
551 self.results.pop(self.namelist.get(i), None)
561 class WirelessSettingsWindow(sd.Dialog):
563 def __init__(self, parent):
564 sd.Dialog.__init__(self, parent, "Wireless settings")
567 def body(self, master):
568 self.configure(background=GetBackground())
569 master.configure(background=GetBackground())
570 self.ssid = tk.StringVar()
571 self.password = tk.StringVar()
573 a = ttk.Label(master, text='SSID :', background=GetBackground())
574 a.grid(row=0, column=0, sticky=tk.E)
575 a.configure(background=GetBackground())
576 ttk.Entry(master, textvariable=self.ssid).grid(row=0, column=1, sticky=tk.W+tk.E, padx=5)
578 ttk.Label(master, text='Password :').grid(row=1, column=0, sticky=tk.E)
579 ttk.Entry(master, textvariable=self.password).grid(row=1, column=1, sticky=tk.W+tk.E, padx=5)
581 self.transient(self.parent)
592 return (self.ssid.get(), self.password.get())
595 class ProjectWindow(tk.Frame):
597 def __init__(self, parent, sdkpath, args):
598 tk.Frame.__init__(self, parent)
600 self.sdkpath = sdkpath
601 self.init_window(args)
602 self.configs = dict()
604 self.password = str()
606 def setState(self, thing, state):
607 for child in thing.winfo_children():
608 child.configure(state=state)
610 def boardtype_change_callback(self, event):
611 boardtype = self.boardtype.get()
612 if boardtype == "pico_w":
613 self.setState(self.picowSubframe, "enabled")
615 self.setState(self.picowSubframe, "disabled")
617 def wirelessSettings(self):
618 result = WirelessSettingsWindow(self)
619 self.ssid, self.password = result.get()
621 def init_window(self, args):
622 self.master.title("Raspberry Pi Pico Project Generator")
623 self.master.configure(bg=GetBackground())
627 mainFrame = tk.Frame(self, bg=GetBackground()).grid(row=optionsRow, column=0, columnspan=6, rowspan=12)
629 # Need to keep a reference to the image or it will not appear.
630 self.logo = tk.PhotoImage(file=GetFilePath("logo_alpha.gif"))
631 logowidget = ttk.Label(mainFrame, image=self.logo, borderwidth=0, relief="solid").grid(row=0,column=0, columnspan=5, pady=10)
635 namelbl = ttk.Label(mainFrame, text='Project Name :').grid(row=optionsRow, column=0, sticky=tk.E)
636 self.projectName = tk.StringVar()
638 if args.name != None:
639 self.projectName.set(args.name)
641 self.projectName.set('ProjectName')
643 nameEntry = ttk.Entry(mainFrame, textvariable=self.projectName).grid(row=optionsRow, column=1, sticky=tk.W+tk.E, padx=5)
647 locationlbl = ttk.Label(mainFrame, text='Location :').grid(row=optionsRow, column=0, sticky=tk.E)
648 self.locationName = tk.StringVar()
649 self.locationName.set(os.getcwd())
650 locationEntry = ttk.Entry(mainFrame, textvariable=self.locationName).grid(row=optionsRow, column=1, columnspan=3, sticky=tk.W+tk.E, padx=5)
651 locationBrowse = ttk.Button(mainFrame, text='Browse', command=self.browse).grid(row=3, column=4)
655 ttk.Label(mainFrame, text = "Board Type :").grid(row=optionsRow, column=0, padx=4, sticky=tk.E)
656 self.boardtype = ttk.Combobox(mainFrame, values=boardtype_list, )
657 self.boardtype.grid(row=4, column=1, padx=4, sticky=tk.W+tk.E)
658 self.boardtype.set('pico')
659 self.boardtype.bind('<<ComboboxSelected>>',self.boardtype_change_callback)
663 featuresframe = ttk.LabelFrame(mainFrame, text="Library Options", relief=tk.RIDGE, borderwidth=2)
664 featuresframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=5, ipadx=5, padx=5, pady=5, sticky=tk.E+tk.W)
666 s = (len(features_list)/3)
668 self.feature_checkbox_vars = []
671 for i in features_list:
672 var = tk.StringVar(value='') # Off by default for the moment
673 c = features_list[i][GUI_TEXT]
674 cb = ttk.Checkbutton(featuresframe, text = c, var=var, onvalue=i, offvalue='')
675 cb.grid(row=row, column=col, padx=15, pady=2, ipadx=1, ipady=1, sticky=tk.E+tk.W)
676 self.feature_checkbox_vars.append(var)
684 # PicoW options section
685 self.picowSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Pico Wireless Options")
686 self.picowSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
687 self.pico_wireless = tk.StringVar()
691 for i in picow_options_list:
692 rb = ttk.Radiobutton(self.picowSubframe, text=picow_options_list[i][GUI_TEXT], variable=self.pico_wireless, val=i)
693 rb.grid(row=row, column=col, padx=15, pady=1, sticky=tk.E+tk.W)
699 # DOnt actually need any settings at the moment.
700 # ttk.Button(self.picowSubframe, text='Settings', command=self.wirelessSettings).grid(row=0, column=4, padx=5, pady=2, sticky=tk.E)
702 self.setState(self.picowSubframe, "disabled")
706 # output options section
707 ooptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Console Options")
708 ooptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
710 self.wantUART = tk.IntVar()
711 self.wantUART.set(args.uart)
712 ttk.Checkbutton(ooptionsSubframe, text="Console over UART", variable=self.wantUART).grid(row=0, column=0, padx=4, sticky=tk.W)
714 self.wantUSB = tk.IntVar()
715 self.wantUSB.set(args.usb)
716 ttk.Checkbutton(ooptionsSubframe, text="Console over USB (Disables other USB use)", variable=self.wantUSB).grid(row=0, column=1, padx=4, sticky=tk.W)
720 # Code options section
721 coptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Code Options")
722 coptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=3, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
724 self.wantExamples = tk.IntVar()
725 self.wantExamples.set(args.examples)
726 ttk.Checkbutton(coptionsSubframe, text="Add examples for Pico library", variable=self.wantExamples).grid(row=0, column=0, padx=4, sticky=tk.W)
728 self.wantRunFromRAM = tk.IntVar()
729 self.wantRunFromRAM.set(args.runFromRAM)
730 ttk.Checkbutton(coptionsSubframe, text="Run from RAM", variable=self.wantRunFromRAM).grid(row=0, column=1, padx=4, sticky=tk.W)
732 self.wantCPP = tk.IntVar()
733 self.wantCPP.set(args.cpp)
734 ttk.Checkbutton(coptionsSubframe, text="Generate C++", variable=self.wantCPP).grid(row=0, column=3, padx=4, sticky=tk.W)
736 ttk.Button(coptionsSubframe, text="Advanced...", command=self.config).grid(row=0, column=4, sticky=tk.E)
738 self.wantCPPExceptions = tk.IntVar()
739 self.wantCPPExceptions.set(args.cppexceptions)
740 ttk.Checkbutton(coptionsSubframe, text="Enable C++ exceptions", variable=self.wantCPPExceptions).grid(row=1, column=0, padx=4, sticky=tk.W)
742 self.wantCPPRTTI = tk.IntVar()
743 self.wantCPPRTTI.set(args.cpprtti)
744 ttk.Checkbutton(coptionsSubframe, text="Enable C++ RTTI", variable=self.wantCPPRTTI).grid(row=1, column=1, padx=4, sticky=tk.W)
748 # Build Options section
750 boptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="Build Options")
751 boptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
753 self.wantBuild = tk.IntVar()
754 self.wantBuild.set(args.build)
755 ttk.Checkbutton(boptionsSubframe, text="Run build after generation", variable=self.wantBuild).grid(row=0, column=0, padx=4, sticky=tk.W)
757 self.wantOverwrite = tk.IntVar()
758 self.wantOverwrite.set(args.overwrite)
759 ttk.Checkbutton(boptionsSubframe, text="Overwrite existing projects", variable=self.wantOverwrite).grid(row=0, column=1, padx=4, sticky=tk.W)
763 # IDE Options section
765 vscodeoptionsSubframe = ttk.LabelFrame(mainFrame, relief=tk.RIDGE, borderwidth=2, text="IDE Options")
766 vscodeoptionsSubframe.grid_columnconfigure(2, weight=1)
767 vscodeoptionsSubframe.grid(row=optionsRow, column=0, columnspan=5, rowspan=2, padx=5, pady=5, ipadx=5, ipady=3, sticky=tk.E+tk.W)
769 self.wantVSCode = tk.IntVar()
770 if args.project is None:
771 self.wantVSCode.set(False)
773 self.wantVSCode.set('vscode' in args.project)
774 ttk.Checkbutton(vscodeoptionsSubframe, text="Create VSCode project", variable=self.wantVSCode).grid(row=0, column=0, padx=4, sticky=tk.W)
776 ttk.Label(vscodeoptionsSubframe, text = " Debugger:").grid(row=0, column=1, padx=4, sticky=tk.W)
778 self.debugger = ttk.Combobox(vscodeoptionsSubframe, values=debugger_list, state="readonly")
779 self.debugger.grid(row=0, column=2, padx=4, sticky=tk.EW)
780 self.debugger.current(args.debugger)
784 # OK, Cancel, Help section
786 QuitButton = ttk.Button(mainFrame, text="Quit", command=self.quit).grid(row=optionsRow, column=4, stick=tk.E, padx=10, pady=5)
787 OKButton = ttk.Button(mainFrame, text="OK", command=self.OK).grid(row=optionsRow, column=3, padx=4, pady=5, sticky=tk.E)
789 # TODO help not implemented yet
790 # HelpButton = ttk.Button(mainFrame, text="Help", command=self.help).grid(row=optionsRow, column=0, pady=5)
792 # You can set a default path here, replace the string with whereever you want.
793 # self.locationName.set('/home/pi/pico_projects')
795 def GetFeatures(self):
799 for cb in self.feature_checkbox_vars:
804 picow_extra = self.pico_wireless.get()
806 if picow_extra != 'picow_none':
807 features.append(picow_extra)
812 # TODO Check if we want to exit here
816 # OK, grab all the settings from the page, then call the generators
817 projectPath = self.locationName.get()
818 features = self.GetFeatures()
820 if (self.wantVSCode.get()):
821 projects.append("vscode")
824 'sdkPath' : self.sdkpath,
825 'projectRoot' : Path(projectPath),
826 'projectName' : self.projectName.get(),
828 'wantOverwrite' : self.wantOverwrite.get(),
829 'wantBuild' : self.wantBuild.get(),
830 'boardtype' : self.boardtype.get(),
831 'features' : features,
832 'projects' : projects,
833 'configs' : self.configs,
834 'wantRunFromRAM': self.wantRunFromRAM.get(),
835 'wantExamples' : self.wantExamples.get(),
836 'wantUART' : self.wantUART.get(),
837 'wantUSB' : self.wantUSB.get(),
838 'wantCPP' : self.wantCPP.get(),
839 'debugger' : self.debugger.current(),
840 'exceptions' : self.wantCPPExceptions.get(),
841 'rtti' : self.wantCPPRTTI.get(),
843 'password' : self.password,
844 'envSuffix' : self.envSuffix,
847 DoEverything(self, params)
850 name = fd.askdirectory()
851 self.locationName.set(name)
857 # Run the configuration window
858 self.configs = ConfigurationWindow(self, self.configs).get()
860 def CheckPrerequisites():
861 global isMac, isWindows
862 isMac = (platform.system() == 'Darwin')
863 isWindows = (platform.system() == 'Windows')
865 # Do we have a compiler?
866 return shutil.which(COMPILER_NAME)
869 def CheckSDKPath(gui):
870 sdkPath = os.getenv('PICO_SDK_PATH')
873 m = 'Unable to locate the Raspberry Pi Pico SDK, PICO_SDK_PATH is not set'
878 elif not os.path.isdir(sdkPath):
879 m = 'Unable to locate the Raspberry Pi Pico SDK, PICO_SDK_PATH does not point to a directory'
888 def GetFilePath(filename):
889 if os.path.islink(__file__):
890 script_file = os.readlink(__file__)
892 script_file = __file__
893 return os.path.join(os.path.dirname(script_file), filename)
895 def ParseCommandLine():
896 debugger_flags = ', '.join('{} = {}'.format(i, v) for i, v in enumerate(debugger_list))
897 parser = argparse.ArgumentParser(description='Pico Project generator')
898 parser.add_argument("name", nargs="?", help="Name of the project")
899 parser.add_argument("-t", "--tsv", help="Select an alternative pico_configs.tsv file", default=GetFilePath("pico_configs.tsv"))
900 parser.add_argument("-o", "--output", help="Set an alternative CMakeList.txt filename", default="CMakeLists.txt")
901 parser.add_argument("-x", "--examples", action='store_true', help="Add example code for the Pico standard library")
902 parser.add_argument("-l", "--list", action='store_true', help="List available features")
903 parser.add_argument("-c", "--configs", action='store_true', help="List available project configuration items")
904 parser.add_argument("-f", "--feature", action='append', help="Add feature to generated project")
905 parser.add_argument("-over", "--overwrite", action='store_true', help="Overwrite any existing project AND files")
906 parser.add_argument("-b", "--build", action='store_true', help="Build after project created")
907 parser.add_argument("-g", "--gui", action='store_true', help="Run a GUI version of the project generator")
908 parser.add_argument("-p", "--project", action='append', help="Generate projects files for IDE. Options are: vscode")
909 parser.add_argument("-r", "--runFromRAM", action='store_true', help="Run the program from RAM rather than flash")
910 parser.add_argument("-uart", "--uart", action='store_true', default=1, help="Console output to UART (default)")
911 parser.add_argument("-nouart", "--nouart", action='store_true', default=0, help="Disable console output to UART")
912 parser.add_argument("-usb", "--usb", action='store_true', help="Console output to USB (disables other USB functionality")
913 parser.add_argument("-cpp", "--cpp", action='store_true', default=0, help="Generate C++ code")
914 parser.add_argument("-cpprtti", "--cpprtti", action='store_true', default=0, help="Enable C++ RTTI (Uses more memory)")
915 parser.add_argument("-cppex", "--cppexceptions", action='store_true', default=0, help="Enable C++ exceptions (Uses more memory)")
916 parser.add_argument("-d", "--debugger", type=int, help="Select debugger ({})".format(debugger_flags), default=0)
917 parser.add_argument("-board", "--boardtype", action="store", default='pico', help="Select board type (see --boardlist for available boards)")
918 parser.add_argument("-bl", "--boardlist", action="store_true", help="List available board types")
919 parser.add_argument("-cp", "--cpath", help="Override default VSCode compiler path")
920 parser.add_argument("-root", "--projectRoot", help="Override default project root where the new project will be created")
921 parser.add_argument("-envSuffix", "--envSuffix", help="[A suffix ot the PICO_SDK_PATH environment variable]")
922 parser.add_argument("-sdkVersion", "--sdkVersion", help="[A sdkVersion to set in settings]")
924 return parser.parse_args()
927 def GenerateMain(folder, projectName, features, cpp):
930 filename = Path(folder) / (projectName + '.cpp')
932 filename = Path(folder) / (projectName + '.c')
934 file = open(filename, 'w')
936 main = ('#include <stdio.h>\n'
937 '#include "pico/stdlib.h"\n'
944 for feat in features:
945 if (feat in features_list):
946 o = f'#include "{features_list[feat][H_FILE]}"\n'
948 if (feat in stdlib_examples_list):
949 o = f'#include "{stdlib_examples_list[feat][H_FILE]}"\n'
951 if (feat in picow_options_list):
952 o = f'#include "{picow_options_list[feat][H_FILE]}"\n'
958 for feat in features:
959 if (feat in code_fragments_per_feature):
960 for s in code_fragments_per_feature[feat][DEFINES]:
968 ' stdio_init_all();\n\n'
972 # Add any initialisers
974 for feat in features:
975 if (feat in code_fragments_per_feature):
976 for s in code_fragments_per_feature[feat][INITIALISERS]:
977 main += (" " * indent)
982 main += (' puts("Hello, world!");\n\n'
992 def GenerateCMake(folder, params):
994 filename = Path(folder) / CMAKELIST_FILENAME
995 projectName = params['projectName']
996 board_type = params['boardtype']
998 # OK, for the path, CMake will accept forward slashes on Windows, and thats
999 # seemingly a bit easier to handle than the backslashes
1000 p = str(params['sdkPath']).replace('\\','/')
1002 cmake_header1 = (f"# Generated Cmake Pico project file\n\n"
1003 "cmake_minimum_required(VERSION 3.13)\n\n"
1004 "set(CMAKE_C_STANDARD 11)\n"
1005 "set(CMAKE_CXX_STANDARD 17)\n\n"
1006 "# Initialise pico_sdk from installed location\n"
1007 "# (note this can come from environment, CMake cache etc)\n"
1008 "# DO NOT EDIT THE NEXT TWO LINES for RaspberryPiPico VS Code Extension to work \n"
1009 f"set(PICO_SDK_PATH $ENV{{PICO_SDK_PATH_{params['envSuffix']}}})\n"
1010 f"set(PICO_TOOLCHAIN_PATH $ENV{{PICO_TOOLCHAIN_PATH_{params['envSuffix']}}})\n\n"
1011 f"set(PICO_BOARD {board_type} CACHE STRING \"Board type\")\n\n"
1012 "# Pull in Raspberry Pi Pico SDK (must be before project)\n"
1013 "include(pico_sdk_import.cmake)\n\n"
1014 "if (PICO_SDK_VERSION_STRING VERSION_LESS \"1.4.0\")\n"
1015 " message(FATAL_ERROR \"Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}\")\n"
1017 f"project({projectName} C CXX ASM)\n"
1021 "\n# Initialise the Raspberry Pi Pico SDK\n"
1022 "pico_sdk_init()\n\n"
1023 "# Add executable. Default name is the project name, version 0.1\n\n"
1027 file = open(filename, 'w')
1029 file.write(cmake_header1)
1031 if params['exceptions']:
1032 file.write("\nset(PICO_CXX_ENABLE_EXCEPTIONS 1)\n")
1035 file.write("\nset(PICO_CXX_ENABLE_RTTI 1)\n")
1037 file.write(cmake_header3)
1039 # add the preprocessor defines for overall configuration
1040 if params['configs']:
1041 file.write('# Add any PICO_CONFIG entries specified in the Advanced settings\n')
1042 for c, v in params['configs'].items():
1047 file.write(f'add_compile_definitions({c} = {v})\n')
1050 # No GUI/command line to set a different executable name at this stage
1051 executableName = projectName
1053 if params['wantCPP']:
1054 file.write(f'add_executable({projectName} {projectName}.cpp )\n\n')
1056 file.write(f'add_executable({projectName} {projectName}.c )\n\n')
1058 file.write(f'pico_set_program_name({projectName} "{executableName}")\n')
1059 file.write(f'pico_set_program_version({projectName} "0.1")\n\n')
1061 if params['wantRunFromRAM']:
1062 file.write(f'# no_flash means the target is to run from RAM\n')
1063 file.write(f'pico_set_binary_type({projectName} no_flash)\n\n')
1065 # Console output destinations
1066 if params['wantUART']:
1067 file.write(f'pico_enable_stdio_uart({projectName} 1)\n')
1069 file.write(f'pico_enable_stdio_uart({projectName} 0)\n')
1071 if params['wantUSB']:
1072 file.write(f'pico_enable_stdio_usb({projectName} 1)\n\n')
1074 file.write(f'pico_enable_stdio_usb({projectName} 0)\n\n')
1076 # If we need wireless, check for SSID and password
1077 # removed for the moment as these settings are currently only needed for the pico-examples
1078 # but may be required in here at a later date.
1080 if 'ssid' in params or 'password' in params:
1081 file.write('# Add any wireless access point information\n')
1082 file.write(f'target_compile_definitions({projectName} PRIVATE\n')
1083 if 'ssid' in params:
1084 file.write(f'WIFI_SSID=\" {params["ssid"]} \"\n')
1086 file.write(f'WIFI_SSID=\"${WIFI_SSID}\"')
1088 if 'password' in params:
1089 file.write(f'WIFI_PASSWORD=\"{params["password"]}\"\n')
1091 file.write(f'WIFI_PASSWORD=\"${WIFI_PASSWORD}\"')
1094 # Standard libraries
1095 file.write('# Add the standard library to the build\n')
1096 file.write(f'target_link_libraries({projectName}\n')
1097 file.write(" " + STANDARD_LIBRARIES)
1100 # Standard include directories
1101 file.write('# Add the standard include files to the build\n')
1102 file.write(f'target_include_directories({projectName} PRIVATE\n')
1103 file.write(" ${CMAKE_CURRENT_LIST_DIR}\n")
1104 file.write(" ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required\n")
1107 # Selected libraries/features
1108 if (params['features']):
1109 file.write('# Add any user requested libraries\n')
1110 file.write(f'target_link_libraries({projectName} \n')
1111 for feat in params['features']:
1112 if (feat in features_list):
1113 file.write(" " + features_list[feat][LIB_NAME] + '\n')
1114 if (feat in picow_options_list):
1115 file.write(" " + picow_options_list[feat][LIB_NAME] + '\n')
1116 file.write(' )\n\n')
1118 file.write(f'pico_add_extra_outputs({projectName})\n\n')
1123 # Generates the requested project files, if any
1124 def generateProjectFiles(projectPath, projectName, sdkPath, projects, debugger, envSuffix, sdkVersion):
1126 oldCWD = os.getcwd()
1128 os.chdir(projectPath)
1130 debugger = debugger_config_list[debugger]
1131 gdbPath = "arm-none-eabi-gdb" if isWindows else "gdb-multiarch"
1132 # Need to escape windows files paths backslashes
1133 # TODO: env in currently not supported in compilerPath var
1134 #cPath = f"${{env:PICO_TOOLCHAIN_PATH_{envSuffix}}}" + os.path.sep + os.path.basename(str(compilerPath).replace('\\', '\\\\' ))
1135 cPath = str(compilerPath).replace('\\', '\\\\' )
1143 "name": "Pico Debug (Cortex-Debug)",
1144 "cwd": "${{workspaceRoot}}",
1145 "executable": "${{command:raspberry-pi-pico.launchTargetPath}}",
1146 "request": "launch",
1147 "type": "cortex-debug",
1148 "servertype": "openocd",
1149 "gdbPath": "{gdbPath}",
1152 "interface/{debugger}",
1155 "svdFile": "${{command:raspberry-pi-pico.getSDKPath}}/src/rp2040/hardware_regs/rp2040.svd",
1156 "runToEntryPoint": "main",
1157 // Give restart the same functionality as runToEntryPoint - main
1158 "postRestartCommands": [
1162 "openOCDLaunchCommands": [
1163 "adapter speed 1000"
1165 "preLaunchTask": "Compile Project"
1168 "name": "Pico Debug (Cortex-Debug with external OpenOCD)",
1169 "cwd": "${{workspaceRoot}}",
1170 "executable": "${{command:raspberry-pi-pico.launchTargetPath}}",
1171 "request": "launch",
1172 "type": "cortex-debug",
1173 "servertype": "external",
1174 "gdbTarget": "localhost:3333",
1175 "gdbPath": "{gdbPath}",
1177 "svdFile": "${{command:raspberry-pi-pico.getSDKPath}}/src/rp2040/hardware_regs/rp2040.svd",
1178 "runToEntryPoint": "main",
1179 // Give restart the same functionality as runToEntryPoint - main
1180 "postRestartCommands": [
1184 "preLaunchTask": "Compile Project"
1187 "name": "Pico Debug (C++ Debugger)",
1189 "request": "launch",
1190 "cwd": "${{workspaceRoot}}",
1191 "program": "${{command:raspberry-pi-pico.launchTargetPath}}",
1193 "miDebuggerPath": "{gdbPath}",
1194 "miDebuggerServerAddress": "localhost:3333",
1195 "debugServerPath": "openocd",
1196 "debugServerArgs": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \\"adapter speed 1000\\"",
1197 "serverStarted": "Listening on port .* for gdb connections",
1198 "filterStderr": true,
1199 "stopAtEntry": true,
1200 "hardwareBreakpoints": {{
1204 "preLaunchTask": "Flash",
1205 "svdPath": "${{command:raspberry-pi-pico.getSDKPath}}/src/rp2040/hardware_regs/rp2040.svd"
1216 "${{workspaceFolder}}/**",
1217 "${{env:PICO_SDK_PATH_{envSuffix}}}/**"
1220 "compilerPath": "{cPath}",
1222 "cppStandard": "c++14",
1223 "intelliSenseMode": "linux-gcc-arm"
1233 "cmake.statusbar.visibility": "hidden",
1234 "cmake.configureOnEdit": false,
1235 "cmake.automaticReconfigure": false,
1236 "cmake.configureOnOpen": false,
1237 "cmake.generator": "Ninja",
1238 "raspberry-pi-pico.sdk": "{sdkVersion}",
1239 "raspberry-pi-pico.envSuffix": "{envSuffix}",
1245 "recommendations": [
1246 "marus25.cortex-debug",
1247 "ms-vscode.cpptools",
1248 "ms-vscode.cpptools-extension-pack",
1249 "ms-vscode.vscode-serial-monitor",
1250 "paulober.raspberry-pi-pico",
1258 "label": "Compile Project",
1261 "args": ["-C", "${{workspaceFolder}}/build"],
1265 "panel": "dedicated"
1267 "problemMatcher": "$gcc"
1273 # Create a build folder, and run our cmake project build from it
1274 if not os.path.exists(VSCODE_FOLDER):
1275 os.mkdir(VSCODE_FOLDER)
1277 os.chdir(VSCODE_FOLDER)
1279 file = open(VSCODE_TASKS_FILENAME, 'w')
1283 filename = VSCODE_LAUNCH_FILENAME
1284 file = open(filename, 'w')
1288 file = open(VSCODE_C_PROPERTIES_FILENAME, 'w')
1289 file.write(properties)
1292 file = open(VSCODE_SETTINGS_FILENAME, 'w')
1293 file.write(settings)
1296 file = open(VSCODE_EXTENSIONS_FILENAME, 'w')
1297 file.write(extensions)
1301 print('Unknown project type requested')
1306 def LoadConfigurations():
1308 with open(args.tsv) as tsvfile:
1309 reader = csv.DictReader(tsvfile, dialect='excel-tab')
1311 configuration_dictionary.append(row)
1313 print("No Pico configurations file found. Continuing without")
1315 def LoadBoardTypes(sdkPath):
1316 # Scan the boards folder for all header files, extract filenames, and make a list of the results
1317 # default folder is <PICO_SDK_PATH>/src/boards/include/boards/*
1318 # If the PICO_BOARD_HEADER_DIRS environment variable is set, use that as well
1320 loc = sdkPath / "src/boards/include/boards"
1322 for x in Path(loc).iterdir():
1323 if x.suffix == '.h':
1324 boards.append(x.stem)
1326 loc = os.getenv('PICO_BOARD_HEADER_DIRS')
1329 for x in Path(loc).iterdir():
1330 if x.suffix == '.h':
1331 boards.append(x.stem)
1335 def DoEverything(parent, params):
1337 if not os.path.exists(params['projectRoot']):
1338 if params['wantGUI']:
1339 mb.showerror('Raspberry Pi Pico Project Generator', 'Invalid project path. Select a valid path and try again')
1342 print('Invalid project path')
1345 oldCWD = os.getcwd()
1346 os.chdir(params['projectRoot'])
1348 # Create our project folder as subfolder
1349 os.makedirs(params['projectName'], exist_ok=True)
1351 os.chdir(params['projectName'])
1353 projectPath = params['projectRoot'] / params['projectName']
1355 # First check if there is already a project in the folder
1356 # If there is we abort unless the overwrite flag it set
1357 if os.path.exists(CMAKELIST_FILENAME):
1358 if not params['wantOverwrite'] :
1359 if params['wantGUI']:
1360 # We can ask the user if they want to overwrite
1361 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')
1365 print('There already appears to be a project in this folder. Use the --overwrite option to overwrite the existing project')
1368 # We should really confirm the user wants to overwrite
1369 #print('Are you sure you want to overwrite the existing project files? (y/N)')
1370 #c = input().split(" ")[0]
1371 #if c != 'y' and c != 'Y' :
1374 # Copy the SDK finder cmake file to our project folder
1375 # Can be found here <PICO_SDK_PATH>/external/pico_sdk_import.cmake
1376 shutil.copyfile(params['sdkPath'] / 'external' / 'pico_sdk_import.cmake', projectPath / 'pico_sdk_import.cmake' )
1378 if params['features']:
1379 features_and_examples = params['features'][:]
1381 features_and_examples= []
1383 if params['wantExamples']:
1384 features_and_examples = list(stdlib_examples_list.keys()) + features_and_examples
1386 GenerateMain('.', params['projectName'], features_and_examples, params['wantCPP'])
1388 GenerateCMake('.', params)
1390 # If we have any ancilliary files, copy them to our project folder
1391 # Currently only the picow with lwIP support needs an extra file, so just check that list
1392 for feat in features_and_examples:
1393 if feat in picow_options_list:
1394 if picow_options_list[feat][ANCILLARY_FILE] != "":
1395 shutil.copy(sourcefolder + "/" + picow_options_list[feat][ANCILLARY_FILE], projectPath / picow_options_list[feat][ANCILLARY_FILE])
1397 # Create a build folder, and run our cmake project build from it
1398 if not os.path.exists('build'):
1403 # If we are overwriting a previous project, we should probably clear the folder, but that might delete something the users thinks is important, so
1404 # for the moment, just delete the CMakeCache.txt file as certain changes may need that to be recreated.
1406 if os.path.exists(CMAKECACHE_FILENAME):
1407 os.remove(CMAKECACHE_FILENAME)
1409 cpus = os.cpu_count()
1414 # Had a special case report, when using MinGW, need to check if using nmake or mingw32-make.
1415 if shutil.which("mingw32-make"):
1416 # Assume MinGW environment
1417 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G "MinGW Makefiles" ..'
1418 makeCmd = 'mingw32-make '
1419 elif shutil.which("ninja"):
1420 # When installing SDK version 1.5.0 on windows with installer pico-setup-windows-x64-standalone.exe, ninja is used
1421 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja ..'
1424 # Everything else assume nmake
1425 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G "NMake Makefiles" ..'
1428 # Ninja now works OK under Linux, so if installed use it by default. It's faster.
1429 if shutil.which("ninja"):
1430 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja ..'
1433 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug ..'
1434 makeCmd = 'make -j' + str(cpus)
1436 if params['wantGUI']:
1437 RunCommandInWindow(parent, cmakeCmd)
1441 if params['projects']:
1442 generateProjectFiles(projectPath, params['projectName'], params['sdkPath'], params['projects'], params['debugger'], params["envSuffix"], params["sdkVersion"])
1444 if params['wantBuild']:
1445 if params['wantGUI']:
1446 RunCommandInWindow(parent, makeCmd)
1449 print('\nIf the application has built correctly, you can now transfer it to the Raspberry Pi Pico board')
1454 ###################################################################################
1455 # main execution starteth here
1457 sourcefolder = os.path.dirname(os.path.abspath(__file__))
1459 args = ParseCommandLine()
1464 if args.debugger > len(debugger_list) - 1:
1467 # Check we have everything we need to compile etc
1468 c = CheckPrerequisites()
1470 ## TODO Do both warnings in the same error message so user does have to keep coming back to find still more to do
1473 m = f'Unable to find the `{COMPILER_NAME}` compiler\n'
1474 m +='You will need to install an appropriate compiler to build a Raspberry Pi Pico project\n'
1475 m += 'See the Raspberry Pi Pico documentation for how to do this on your particular platform\n'
1483 if args.name == None and not args.gui and not args.list and not args.configs and not args.boardlist:
1484 print("No project name specfied\n")
1487 # Check if we were provided a compiler path, and override the default if so
1489 compilerPath = Path(args.cpath)
1491 compilerPath = Path(c)
1493 # load/parse any configuration dictionary we may have
1494 LoadConfigurations()
1496 p = CheckSDKPath(args.gui)
1503 boardtype_list = LoadBoardTypes(sdkPath)
1504 boardtype_list.sort()
1507 RunGUI(sdkPath, args) # does not return, only exits
1509 projectRoot = Path(os.getcwd()) if not args.projectRoot else Path(args.projectRoot)
1511 if args.list or args.configs or args.boardlist:
1513 print("Available project features:\n")
1514 for feat in features_list:
1515 print(feat.ljust(6), '\t', features_list[feat][GUI_TEXT])
1519 print("Available project configuration items:\n")
1520 for conf in configuration_dictionary:
1521 print(conf['name'].ljust(40), '\t', conf['description'])
1525 print("Available board types:\n")
1526 for board in boardtype_list:
1533 'sdkPath' : sdkPath,
1534 'projectRoot' : projectRoot,
1535 'projectName' : args.name,
1537 'wantOverwrite' : args.overwrite,
1538 'boardtype' : args.boardtype,
1539 'wantBuild' : args.build,
1540 'features' : args.feature,
1541 'projects' : args.project,
1543 'wantRunFromRAM': args.runFromRAM,
1544 'wantExamples' : args.examples,
1545 'wantUART' : args.uart,
1546 'wantUSB' : args.usb,
1547 'wantCPP' : args.cpp,
1548 'debugger' : args.debugger,
1549 'exceptions' : args.cppexceptions,
1550 'rtti' : args.cpprtti,
1553 'envSuffix' : args.envSuffix,
1554 'sdkVersion' : args.sdkVersion,
1557 DoEverything(None, params)