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_FOLDER='.vscode'
39 CONFIG_UNSET="Not set"
41 # Standard libraries for all builds
42 # And any more to string below, space separator
43 STANDARD_LIBRARIES = 'pico_stdlib'
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
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"),
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"),
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")
78 debugger_list = ["DebugProbe (CMSIS-DAP)", "SWD (Pi host)"]
79 debugger_config_list = ["cmsis-dap.cfg", "raspberrypi-swd.cfg"]
83 # Could add an extra item that shows how to use some of the available functions for the feature
86 # This also contains example code for the standard library (see stdlib_examples_list)
87 code_fragments_per_feature = {
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" ),
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);", "" )
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",
112 "#define PIN_SCK 18",
113 "#define PIN_MOSI 19" ),
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);", "")
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",
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);"
146 "// Example uses GPIO 2",
150 "// GPIO initialisation.",
151 "// We will make this GPIO an input, and pull it up by default",
153 "gpio_set_dir(GPIO, GPIO_IN);",
154 "gpio_pull_up(GPIO);","",
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);",
172 "int64_t alarm_callback(alarm_id_t id, void *user_data) {",
173 " // Put your timeout handler code in here",
178 "// Timer example code - This example fires off the callback after 2000ms",
179 "add_alarm_in_ms(2000, alarm_callback, NULL, false);"
185 "// Watchdog example code",
186 "if (watchdog_caused_reboot()) {",
187 " // Whatever action you may take if a watchdog caused a reboot",
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();",
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));"
217 configuration_dictionary = list(dict())
221 compilerPath = Path("/usr/bin/arm-none-eabi-gcc")
226 def GetButtonBackground():
232 def GetButtonTextColour():
235 def RunGUI(sdkpath, args):
237 style = ttk.Style(root)
238 style.theme_use('default')
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() )
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())])
255 app = ProjectWindow(root, sdkpath, args)
257 app.configure(background=GetBackground())
262 def RunWarning(message):
263 mb.showwarning('Raspberry Pi Pico Project Generator', message)
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,''):
274 ok["state"] = tk.NORMAL
276 text.insert(tk.END, line)
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)
284 self.init_window(title)
286 def init_window(self, title):
289 frame = tk.Frame(self, borderwidth=5, relief=tk.RIDGE)
290 frame.pack(fill=tk.X, expand=True, side=tk.TOP)
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)
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
306 self.transient(self.parent)
313 def RunCommandInWindow(parent, command):
314 w = DisplayWindow(parent, command)
315 x = threading.Thread(target=thread_function, args=(w.text, command, w.OKButton))
317 parent.wait_window(w)
319 class EditBoolWindow(sd.Dialog):
321 def __init__(self, parent, configitem, current):
323 self.config_item = configitem
324 self.current = current
325 sd.Dialog.__init__(self, parent, "Edit boolean configuration")
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)
338 return self.result.get()
340 class EditIntWindow(sd.Dialog):
342 def __init__(self, parent, configitem, current):
344 self.config_item = configitem
345 self.current = current
346 sd.Dialog.__init__(self, parent, "Edit integer configuration")
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)
358 self.result = self.input.get()
359 # Check for numeric entry
363 self.result = CONFIG_UNSET
369 class EditEnumWindow(sd.Dialog):
370 def __init__(self, parent, configitem, current):
372 self.config_item = configitem
373 self.current = current
374 sd.Dialog.__init__(self, parent, "Edit Enumeration configuration")
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)
385 self.result = self.input.get()
392 class ConfigurationWindow(tk.Toplevel):
394 def __init__(self, parent, initial_config):
395 tk.Toplevel.__init__(self, parent)
397 self.results = initial_config
398 self.init_window(self)
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)
411 okButton = ttk.Button(self, text="OK", command=self.ok)
412 cancelButton = ttk.Button(self, text="Cancel", command=self.cancel)
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)
421 self.descriptionText = tk.Text(self, state=tk.DISABLED, height=2)
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]
426 scroll = tk.Scrollbar(self, orient=tk.VERTICAL, command=self.yview)
428 for box in self.listlist:
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)
440 scroll.grid(column=7, sticky=tk.N + tk.S)
443 for box in self.listlist:
444 box.grid(row=2, column=i, padx=0, sticky=tk.W + tk.E)
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)
451 # populate the list box with our config options
452 for conf in configuration_dictionary:
453 self.namelist.insert(tk.END, conf['name'])
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'])
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'})
468 def yview(self, *args):
469 for box in self.listlist:
472 def mousewheel(self, event):
473 if (event.num == 4): # Linux encodes wheel as 'buttons' 4 and 5
475 elif (event.num == 5):
477 else: # Windows & OSX
480 for box in self.listlist:
481 box.yview("scroll", delta, "units")
484 def changeSelection(self, evt):
486 sellist = box.curselection()
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)
500 # Set all the other list boxes to the same index
501 for b in self.listlist:
503 b.selection_clear(0, tk.END)
504 b.selection_set(index)
506 def OnEntryUpDown(self, event):
508 selection = box.curselection()
511 index = int(selection[0])
512 if event.keysym == 'Up':
514 elif event.keysym == 'Down':
517 if 0 <= index < box.size():
518 for b in self.listlist:
519 b.selection_clear(0, tk.END)
520 b.selection_set(index)
523 def doubleClick(self, evt):
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()
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'})
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
550 self.results.pop(self.namelist.get(i), None)
560 class WirelessSettingsWindow(sd.Dialog):
562 def __init__(self, parent):
563 sd.Dialog.__init__(self, parent, "Wireless settings")
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()
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)
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)
580 self.transient(self.parent)
591 return (self.ssid.get(), self.password.get())
594 class ProjectWindow(tk.Frame):
596 def __init__(self, parent, sdkpath, args):
597 tk.Frame.__init__(self, parent)
599 self.sdkpath = sdkpath
600 self.init_window(args)
601 self.configs = dict()
603 self.password = str()
605 def setState(self, thing, state):
606 for child in thing.winfo_children():
607 child.configure(state=state)
609 def boardtype_change_callback(self, event):
610 boardtype = self.boardtype.get()
611 if boardtype == "pico_w":
612 self.setState(self.picowSubframe, "enabled")
614 self.setState(self.picowSubframe, "disabled")
616 def wirelessSettings(self):
617 result = WirelessSettingsWindow(self)
618 self.ssid, self.password = result.get()
620 def init_window(self, args):
621 self.master.title("Raspberry Pi Pico Project Generator")
622 self.master.configure(bg=GetBackground())
626 mainFrame = tk.Frame(self, bg=GetBackground()).grid(row=optionsRow, column=0, columnspan=6, rowspan=12)
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)
634 namelbl = ttk.Label(mainFrame, text='Project Name :').grid(row=optionsRow, column=0, sticky=tk.E)
635 self.projectName = tk.StringVar()
637 if args.name != None:
638 self.projectName.set(args.name)
640 self.projectName.set('ProjectName')
642 nameEntry = ttk.Entry(mainFrame, textvariable=self.projectName).grid(row=optionsRow, column=1, sticky=tk.W+tk.E, padx=5)
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)
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)
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)
665 s = (len(features_list)/3)
667 self.feature_checkbox_vars = []
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)
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()
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)
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)
701 self.setState(self.picowSubframe, "disabled")
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)
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)
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)
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)
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)
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)
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)
735 ttk.Button(coptionsSubframe, text="Advanced...", command=self.config).grid(row=0, column=4, sticky=tk.E)
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)
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)
747 # Build Options section
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)
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)
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)
762 # IDE Options section
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)
768 self.wantVSCode = tk.IntVar()
769 if args.project is None:
770 self.wantVSCode.set(False)
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)
775 ttk.Label(vscodeoptionsSubframe, text = " Debugger:").grid(row=0, column=1, padx=4, sticky=tk.W)
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)
783 # OK, Cancel, Help section
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)
788 # TODO help not implemented yet
789 # HelpButton = ttk.Button(mainFrame, text="Help", command=self.help).grid(row=optionsRow, column=0, pady=5)
791 # You can set a default path here, replace the string with whereever you want.
792 # self.locationName.set('/home/pi/pico_projects')
794 def GetFeatures(self):
798 for cb in self.feature_checkbox_vars:
803 picow_extra = self.pico_wireless.get()
805 if picow_extra != 'picow_none':
806 features.append(picow_extra)
811 # TODO Check if we want to exit here
815 # OK, grab all the settings from the page, then call the generators
816 projectPath = self.locationName.get()
817 features = self.GetFeatures()
819 if (self.wantVSCode.get()):
820 projects.append("vscode")
823 'sdkPath' : self.sdkpath,
824 'projectRoot' : Path(projectPath),
825 'projectName' : self.projectName.get(),
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(),
842 'password' : self.password,
845 DoEverything(self, params)
848 name = fd.askdirectory()
849 self.locationName.set(name)
855 # Run the configuration window
856 self.configs = ConfigurationWindow(self, self.configs).get()
858 def CheckPrerequisites():
859 global isMac, isWindows
860 isMac = (platform.system() == 'Darwin')
861 isWindows = (platform.system() == 'Windows')
863 # Do we have a compiler?
864 return shutil.which(COMPILER_NAME)
867 def CheckSDKPath(gui):
868 sdkPath = os.getenv('PICO_SDK_PATH')
871 m = 'Unable to locate the Raspberry Pi Pico SDK, PICO_SDK_PATH is not set'
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'
886 def GetFilePath(filename):
887 if os.path.islink(__file__):
888 script_file = os.readlink(__file__)
890 script_file = __file__
891 return os.path.join(os.path.dirname(script_file), filename)
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")
920 return parser.parse_args()
923 def GenerateMain(folder, projectName, features, cpp):
926 filename = Path(folder) / (projectName + '.cpp')
928 filename = Path(folder) / (projectName + '.c')
930 file = open(filename, 'w')
932 main = ('#include <stdio.h>\n'
933 '#include "pico/stdlib.h"\n'
940 for feat in features:
941 if (feat in features_list):
942 o = f'#include "{features_list[feat][H_FILE]}"\n'
944 if (feat in stdlib_examples_list):
945 o = f'#include "{stdlib_examples_list[feat][H_FILE]}"\n'
947 if (feat in picow_options_list):
948 o = f'#include "{picow_options_list[feat][H_FILE]}"\n'
954 for feat in features:
955 if (feat in code_fragments_per_feature):
956 for s in code_fragments_per_feature[feat][DEFINES]:
964 ' stdio_init_all();\n\n'
968 # Add any initialisers
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)
978 main += (' puts("Hello, world!");\n\n'
988 def GenerateCMake(folder, params):
990 filename = Path(folder) / CMAKELIST_FILENAME
991 projectName = params['projectName']
992 board_type = params['boardtype']
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('\\','/')
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"
1012 f"project({projectName} C CXX ASM)\n"
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"
1022 file = open(filename, 'w')
1024 file.write(cmake_header1)
1026 if params['exceptions']:
1027 file.write("\nset(PICO_CXX_ENABLE_EXCEPTIONS 1)\n")
1030 file.write("\nset(PICO_CXX_ENABLE_RTTI 1)\n")
1032 file.write(cmake_header3)
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():
1042 file.write(f'add_compile_definitions({c} = {v})\n')
1045 # No GUI/command line to set a different executable name at this stage
1046 executableName = projectName
1048 if params['wantCPP']:
1049 file.write(f'add_executable({projectName} {projectName}.cpp )\n\n')
1051 file.write(f'add_executable({projectName} {projectName}.c )\n\n')
1053 file.write(f'pico_set_program_name({projectName} "{executableName}")\n')
1054 file.write(f'pico_set_program_version({projectName} "0.1")\n\n')
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')
1060 # Console output destinations
1061 if params['wantUART']:
1062 file.write(f'pico_enable_stdio_uart({projectName} 1)\n')
1064 file.write(f'pico_enable_stdio_uart({projectName} 0)\n')
1066 if params['wantUSB']:
1067 file.write(f'pico_enable_stdio_usb({projectName} 1)\n\n')
1069 file.write(f'pico_enable_stdio_usb({projectName} 0)\n\n')
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.
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')
1081 file.write(f'WIFI_SSID=\"${WIFI_SSID}\"')
1083 if 'password' in params:
1084 file.write(f'WIFI_PASSWORD=\"{params["password"]}\"\n')
1086 file.write(f'WIFI_PASSWORD=\"${WIFI_PASSWORD}\"')
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)
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")
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')
1113 file.write(f'pico_add_extra_outputs({projectName})\n\n')
1118 # Generates the requested project files, if any
1119 def generateProjectFiles(projectPath, projectName, sdkPath, projects, debugger):
1121 oldCWD = os.getcwd()
1123 os.chdir(projectPath)
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('\\', '\\\\' )
1133 ' "version": "0.2.0",\n'
1134 ' "configurations": [\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'
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'
1155 ' "openOCDLaunchCommands": [\n'
1156 ' "adapter speed 1000"\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'
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'
1195 ' "preLaunchTask": "Flash",\n'
1196 ' "svdPath": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd"\n'
1202 ' "configurations": [\n'
1204 ' "name": "Pico",\n'
1205 ' "includePath": [\n'
1206 ' "${workspaceFolder}/**",\n'
1207 ' "${env:PICO_SDK_PATH}/**"\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'
1221 ' "cmake.statusbar.advanced": {\n'
1223 ' "visibility": "hidden"\n'
1226 ' "visibility": "hidden"\n'
1229 ' "visibility": "hidden"\n'
1231 ' "buildTarget": {\n'
1232 ' "visibility": "hidden"\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'
1240 ' "cmake.generator": "Ninja",\n'
1241 ' "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"\n'
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'
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)
1258 os.chdir(VSCODE_FOLDER)
1260 filename = VSCODE_LAUNCH_FILENAME
1261 file = open(filename, 'w')
1265 file = open(VSCODE_C_PROPERTIES_FILENAME, 'w')
1266 file.write(properties)
1269 file = open(VSCODE_SETTINGS_FILENAME, 'w')
1270 file.write(settings)
1273 file = open(VSCODE_EXTENSIONS_FILENAME, 'w')
1274 file.write(extensions)
1278 print('Unknown project type requested')
1283 def LoadConfigurations():
1285 with open(args.tsv) as tsvfile:
1286 reader = csv.DictReader(tsvfile, dialect='excel-tab')
1288 configuration_dictionary.append(row)
1290 print("No Pico configurations file found. Continuing without")
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
1297 loc = sdkPath / "src/boards/include/boards"
1299 for x in Path(loc).iterdir():
1300 if x.suffix == '.h':
1301 boards.append(x.stem)
1303 loc = os.getenv('PICO_BOARD_HEADER_DIRS')
1306 for x in Path(loc).iterdir():
1307 if x.suffix == '.h':
1308 boards.append(x.stem)
1312 def DoEverything(parent, params):
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')
1319 print('Invalid project path')
1322 oldCWD = os.getcwd()
1323 os.chdir(params['projectRoot'])
1325 # Create our project folder as subfolder
1326 os.makedirs(params['projectName'], exist_ok=True)
1328 os.chdir(params['projectName'])
1330 projectPath = params['projectRoot'] / params['projectName']
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')
1342 print('There already appears to be a project in this folder. Use the --overwrite option to overwrite the existing project')
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' :
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' )
1355 if params['features']:
1356 features_and_examples = params['features'][:]
1358 features_and_examples= []
1360 if params['wantExamples']:
1361 features_and_examples = list(stdlib_examples_list.keys()) + features_and_examples
1363 GenerateMain('.', params['projectName'], features_and_examples, params['wantCPP'])
1365 GenerateCMake('.', params)
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])
1374 # Create a build folder, and run our cmake project build from it
1375 if not os.path.exists('build'):
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.
1383 if os.path.exists(CMAKECACHE_FILENAME):
1384 os.remove(CMAKECACHE_FILENAME)
1386 cpus = os.cpu_count()
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 ..'
1401 # Everything else assume nmake
1402 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug -G "NMake Makefiles" ..'
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 ..'
1410 cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug ..'
1411 makeCmd = 'make -j' + str(cpus)
1413 if params['wantGUI']:
1414 RunCommandInWindow(parent, cmakeCmd)
1418 if params['projects']:
1419 generateProjectFiles(projectPath, params['projectName'], params['sdkPath'], params['projects'], params['debugger'])
1421 if params['wantBuild']:
1422 if params['wantGUI']:
1423 RunCommandInWindow(parent, makeCmd)
1426 print('\nIf the application has built correctly, you can now transfer it to the Raspberry Pi Pico board')
1431 ###################################################################################
1432 # main execution starteth here
1434 sourcefolder = os.path.dirname(os.path.abspath(__file__))
1436 args = ParseCommandLine()
1441 if args.debugger > len(debugger_list) - 1:
1444 # Check we have everything we need to compile etc
1445 c = CheckPrerequisites()
1447 ## TODO Do both warnings in the same error message so user does have to keep coming back to find still more to do
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'
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")
1464 # Check if we were provided a compiler path, and override the default if so
1466 compilerPath = Path(args.cpath)
1468 compilerPath = Path(c)
1470 # load/parse any configuration dictionary we may have
1471 LoadConfigurations()
1473 p = CheckSDKPath(args.gui)
1480 boardtype_list = LoadBoardTypes(sdkPath)
1481 boardtype_list.sort()
1484 RunGUI(sdkPath, args) # does not return, only exits
1486 projectRoot = Path(os.getcwd()) if not args.projectRoot else Path(args.projectRoot)
1488 if args.list or args.configs or args.boardlist:
1490 print("Available project features:\n")
1491 for feat in features_list:
1492 print(feat.ljust(6), '\t', features_list[feat][GUI_TEXT])
1496 print("Available project configuration items:\n")
1497 for conf in configuration_dictionary:
1498 print(conf['name'].ljust(40), '\t', conf['description'])
1502 print("Available board types:\n")
1503 for board in boardtype_list:
1510 'sdkPath' : sdkPath,
1511 'projectRoot' : projectRoot,
1512 'projectName' : args.name,
1514 'wantOverwrite' : args.overwrite,
1515 'boardtype' : args.boardtype,
1516 'wantBuild' : args.build,
1517 'features' : args.feature,
1518 'projects' : args.project,
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,
1532 DoEverything(None, params)