├── .gitignore ├── LICENSE ├── README.md ├── build.zig ├── example └── main.zig ├── picosdk.zig └── src ├── Library.zig ├── cmake.zig ├── entry.c ├── pico.zig ├── sdk.zig └── util.zig /.gitignore: -------------------------------------------------------------------------------- 1 | zig-out 2 | zig-cache 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Jorn Veken 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##### Update 2 | Going to do a complete rewrite as this doesn't work on Zig 0.10.x, archiving. 3 | 4 | ###### - Paperdev 5 | 6 | # Zig on the Raspberry Pi Pico 7 | Zig on the Raspberry Pi Pico without losing access to the SDK. 8 | 9 | ## Requirements 10 | Install the Pico SDK dependencies 11 | ```sh 12 | sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib 13 | ``` 14 | 15 | Clone the Pico SDK 16 | ```sh 17 | git clone https://github.com/raspberrypi/pico-sdk path/to/pico-sdk 18 | ``` 19 | 20 | Make sure `PICO_SDK_PATH` is set 21 | ```sh 22 | export PICO_SDK_PATH path/to/pico-sdk 23 | ``` 24 | 25 | ## Usage 26 | Check `build.zig` and `example/main.zig`, it's fairly self explanatory. 27 | 28 | ## Build 29 | To build the example for the Pico W 30 | ``` 31 | zig build 32 | ``` 33 | 34 | ## Running 35 | If you have picotool installed, load the resulting `uf2` file 36 | ``` 37 | picotool load -f zig-out/uf2/pico-app.uf2 38 | ``` 39 | 40 | ## Todo 41 | - [x] integrate cmake into zig build 42 | - [x] add include paths of pico libraries to app 43 | - [ ] optimize cmake build steps 44 | - [ ] wrap pico-sdk functions into Pkgs 45 | - [ ] ??? 46 | - [ ] profit 47 | 48 | ### License 49 | MIT 50 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const picosdk = @import("picosdk.zig"); 3 | 4 | pub fn build(b: *std.build.Builder) void { 5 | const mode = b.standardReleaseOptions(); 6 | 7 | const pico_app = picosdk.addPicoApp( 8 | b, 9 | "pico-app", 10 | "example/main.zig", 11 | .{ .pico_w = .{ .cyw43_arch = .threadsafe_background } }, 12 | &.{ 13 | picosdk.pico_stdlib, 14 | picosdk.pico_cyw43_arch_none, 15 | }, 16 | ); 17 | 18 | pico_app.zig.setBuildMode(mode); 19 | pico_app.enable_stdio(.usb); 20 | pico_app.install(); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /example/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const c = @cImport({ 4 | @cInclude("pico/stdlib.h"); 5 | @cInclude("pico/stdio.h"); 6 | @cInclude("pico/cyw43_arch.h"); 7 | }); 8 | 9 | const CYW43_WL_GPIO_LED_PIN = 0; 10 | 11 | var led_state : bool = false; 12 | var total_blinks : i32 = 0; 13 | 14 | export fn init() void { 15 | c.stdio_init_all(); 16 | if (c.cyw43_arch_init() != 1) { 17 | std.log.info("WiFi fail.", .{}); 18 | return; 19 | } 20 | } 21 | 22 | export fn loop() void { 23 | led_state = !led_state; 24 | c.cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_state); 25 | c.sleep_ms(350); 26 | 27 | if (led_state == false) { 28 | std.log.info("Blink! {d}", .{total_blinks}); 29 | total_blinks = total_blinks + 1; 30 | } 31 | } 32 | 33 | pub fn log( 34 | comptime message_level: std.log.Level, 35 | comptime scope: @Type(.EnumLiteral), 36 | comptime format: []const u8, 37 | args: anytype, 38 | ) void { 39 | const level_txt = comptime message_level.asText(); 40 | const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; 41 | 42 | var buffer: [1024]u8 = .{}; 43 | var message = std.fmt.bufPrint(&buffer, level_txt ++ prefix2 ++ format ++ "\n\r", args) catch return; 44 | for (message) |char| { 45 | _ = c.putchar_raw(char); 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /picosdk.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pico = @import("src/pico.zig"); 3 | const sdk = @import("src/sdk.zig"); 4 | const Library = @import("src/Library.zig"); 5 | 6 | const Builder = std.build.Builder; 7 | const PicoAppStep = pico.PicoAppStep; 8 | 9 | pub fn addPicoApp( 10 | builder: *Builder, 11 | name: []const u8, 12 | root_src: []const u8, 13 | board: pico.Board, 14 | libs: []const Library, 15 | ) *PicoAppStep { 16 | return pico.PicoAppStep.create( 17 | builder, 18 | name, 19 | root_src, 20 | board, 21 | libs, 22 | ); 23 | } 24 | 25 | // I want wrappers and packages, so this will likely change 26 | pub usingnamespace struct { 27 | pub const pico_stdlib = Library { 28 | .name = "pico_stdlib", 29 | }; 30 | 31 | pub const pico_cyw43_arch_none = Library { 32 | .name = "pico_cyw43_arch_none", 33 | }; 34 | }; 35 | 36 | -------------------------------------------------------------------------------- /src/Library.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const util = @import("util.zig"); 3 | const cmake = @import("cmake.zig"); 4 | const pico = @import("pico.zig"); 5 | 6 | const Self = @This(); 7 | 8 | const Builder = std.build.Builder; 9 | const Step = std.build.Step; 10 | const LibExeObjStep = std.build.LibExeObjStep; 11 | const ArrayList = std.ArrayList; 12 | 13 | name: []const u8, 14 | 15 | /// Actual library linking is handled by the Pico SDK, 16 | /// so dependencies are not handled here. 17 | pub fn listNames( 18 | allocator: std.mem.Allocator, 19 | libs: []const Self, 20 | ) ![]const u8 { 21 | var list = std.ArrayList(u8).init(allocator); 22 | for (libs) |lib| { 23 | try list.appendSlice(lib.name); 24 | try list.append(' '); 25 | } 26 | return list.toOwnedSlice(); 27 | } 28 | 29 | /// find... all SDK include dirs :P 30 | fn allSdkIncludeDirs(allocator: std.mem.Allocator) !std.ArrayList([]const u8) { 31 | const pico_sdk_path = try util.picoSdkDirPath(); 32 | const pico_sdk_path_src = try std.mem.concat(allocator, u8, &.{ 33 | pico_sdk_path, 34 | std.fs.path.sep_str, 35 | "src", 36 | }); 37 | defer allocator.free(pico_sdk_path_src); 38 | var dirs = ArrayList([]const u8).init(allocator); 39 | var pico_sdk = try std.fs.openIterableDirAbsolute(pico_sdk_path_src, .{}); 40 | defer pico_sdk.close(); 41 | var walker = try pico_sdk.walk(allocator); 42 | while (try walker.next()) |entry| { 43 | if (entry.kind == .Directory) { 44 | if (std.mem.eql(u8, entry.basename, "include")) { 45 | const full_path = try std.mem.concat(allocator, u8, &.{ 46 | pico_sdk_path_src, 47 | std.fs.path.sep_str, 48 | entry.path 49 | }); 50 | try dirs.append(full_path); 51 | } 52 | } 53 | } 54 | // some left overs. 55 | // but just scanning for anything containing a .h seems too much 56 | for ([_][]const u8 { 57 | "lib/cyw43-driver/src", 58 | "lib/tinyusb/src", 59 | "lib/lwip/src/include", 60 | "test/kitchen_sink", 61 | }) |path| { 62 | const absolute_path = try std.mem.concat(allocator, u8, &.{ 63 | pico_sdk_path, 64 | std.fs.path.sep_str, 65 | path, 66 | }); 67 | try dirs.append(absolute_path); 68 | } 69 | 70 | return dirs; 71 | } 72 | 73 | pub const IncludeStep = struct { 74 | 75 | builder: *Builder, 76 | step: Step, 77 | libs: []const Self, 78 | zig: *LibExeObjStep, 79 | cmakelists: *cmake.ListsStep, 80 | board: pico.Board, 81 | 82 | pub fn create( 83 | builder: *Builder, 84 | libs: []const Self, 85 | zig: *LibExeObjStep, 86 | cmakelists: *cmake.ListsStep, 87 | board: pico.Board, 88 | ) *IncludeStep { 89 | const self = builder.allocator.create(IncludeStep) catch unreachable; 90 | self.* = IncludeStep { 91 | .builder = builder, 92 | .step = Step.init( 93 | .custom, 94 | "libraryinclude-step", 95 | builder.allocator, 96 | make, 97 | ), 98 | .libs = libs, 99 | .zig = zig, 100 | .cmakelists = cmakelists, 101 | .board = board, 102 | }; 103 | self.zig.linkLibC(); 104 | self.step.dependOn(&self.cmakelists.step); 105 | self.zig.step.dependOn(&self.step); 106 | return self; 107 | } 108 | 109 | fn make(step: *Step) !void { 110 | const self = @fieldParentPtr(IncludeStep, "step", step); 111 | const allocator = self.builder.allocator; 112 | const include_paths = try allSdkIncludeDirs(allocator); 113 | defer include_paths.deinit(); 114 | // pico sdk include paths 115 | for (include_paths.items) |include_path| { 116 | std.log.scoped(.cInclude).info("{s}", .{include_path}); 117 | self.zig.addIncludeDir(include_path); 118 | } 119 | // pico generated 120 | const generated = try std.mem.concat(allocator, u8, &.{ 121 | self.cmakelists.build_dir.?, 122 | std.fs.path.sep_str, 123 | "generated", 124 | std.fs.path.sep_str, 125 | "pico_base", 126 | }); 127 | defer allocator.free(generated); 128 | self.zig.addSystemIncludeDir(generated); 129 | self.zig.addSystemIncludeDir("/usr/lib/arm-none-eabi/include"); 130 | 131 | switch (self.board) { 132 | .pico_w => |config| { 133 | switch (config.cyw43_arch) { 134 | .threadsafe_background => { 135 | self.zig.defineCMacro( 136 | "PICO_CYW43_ARCH_THREADSAFE_BACKGROUND", 137 | null, 138 | ); 139 | }, 140 | .poll => { 141 | self.zig.defineCMacro( 142 | "PICO_CYW43_ARCH_POLL", 143 | null 144 | ); 145 | }, 146 | } 147 | }, 148 | .pico => |_| {}, 149 | } 150 | } 151 | }; 152 | 153 | -------------------------------------------------------------------------------- /src/cmake.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const util = @import("util.zig"); 3 | 4 | const Builder = std.build.Builder; 5 | const Step = std.build.Step; 6 | const Dir = std.fs.Dir; 7 | const File = std.fs.File; 8 | const InstallArtifactStep = std.build.InstallArtifactStep; 9 | const LibExeObjStep = std.build.LibExeObjStep; 10 | 11 | pub const MakeStep = struct { 12 | const Self = @This(); 13 | 14 | builder: *Builder, 15 | step: Step, 16 | build_dir: ?[]const u8, 17 | cmakebuild: *BuildStep, 18 | 19 | pub fn create( 20 | builder: *Builder, 21 | cmakebuild: *BuildStep, 22 | ) *Self { 23 | const self = builder.allocator.create(Self) catch unreachable; 24 | self.* = Self { 25 | .builder = builder, 26 | .step = Step.init( 27 | .custom, 28 | "cmakemake-step", 29 | builder.allocator, 30 | make, 31 | ), 32 | .build_dir = null, 33 | .cmakebuild = cmakebuild, 34 | }; 35 | self.step.dependOn(&cmakebuild.step); 36 | return self; 37 | } 38 | 39 | fn make(step: *Step) !void { 40 | const self = @fieldParentPtr(Self, "step", step); 41 | if (self.cmakebuild.build_dir) |cwd| { 42 | self.build_dir = cwd; 43 | try util.exec( 44 | self.builder.allocator, 45 | &.{"make"}, 46 | cwd, 47 | .CmakeMake 48 | ); 49 | } 50 | else return error.CmakeBuildDirNull; 51 | 52 | } 53 | }; 54 | 55 | pub const BuildStep = struct { 56 | const Self = @This(); 57 | 58 | builder: *Builder, 59 | step: Step, 60 | build_dir: ?[]const u8, 61 | cmakelists: *ListsStep, 62 | 63 | pub fn create( 64 | builder: *Builder, 65 | cmakelists: *ListsStep, 66 | zig: *LibExeObjStep, 67 | ) *Self { 68 | const self = builder.allocator.create(Self) catch unreachable; 69 | self.* = Self { 70 | .builder = builder, 71 | .step = Step.init( 72 | .custom, 73 | "cmakebuild-step", 74 | builder.allocator, 75 | make, 76 | ), 77 | .build_dir = null, 78 | .cmakelists = cmakelists, 79 | }; 80 | self.step.dependOn(&cmakelists.step); 81 | //self.step.dependOn(zig.install_step.?.step); 82 | zig.step.dependOn(&self.step); 83 | return self; 84 | } 85 | 86 | fn make(step: *Step) !void { 87 | const self = @fieldParentPtr(Self, "step", step); 88 | if (self.cmakelists.path) |path| { 89 | self.build_dir = self.cmakelists.build_dir.?; 90 | try util.exec( 91 | self.builder.allocator, 92 | &.{"cmake", "-S", path, "-B", self.build_dir.?}, 93 | null, 94 | .CmakeBuild 95 | ); 96 | } 97 | else return error.CmakeListsPathNull; 98 | } 99 | }; 100 | 101 | pub const ListsStep = struct { 102 | const Self = @This(); 103 | 104 | const dir_name = "cmake"; 105 | const build_dir_name = "build"; 106 | const txt_name = "CMakeLists.txt"; 107 | 108 | builder: *Builder, 109 | step: Step, 110 | txt: ?[]const u8, 111 | txt_src: ?*?[]const u8, 112 | path: ?[]const u8, 113 | build_dir: ?[]const u8, 114 | 115 | pub fn create( 116 | builder: *Builder, 117 | ) *Self { 118 | const self = builder.allocator.create(Self) catch unreachable; 119 | self.* = Self { 120 | .builder = builder, 121 | .step = Step.init( 122 | .custom, 123 | "cmakelists-step", 124 | builder.allocator, 125 | make, 126 | ), 127 | .txt = null, 128 | .txt_src = null, 129 | .path = null, 130 | .build_dir = null, 131 | }; 132 | return self; 133 | } 134 | 135 | fn make(step: *Step) !void { 136 | const self = @fieldParentPtr(Self, "step", step); 137 | 138 | const cmakelists_txt_contents = 139 | if (self.txt) |txt| txt 140 | else if (self.txt_src) |src| src.* 141 | else null; 142 | 143 | if (cmakelists_txt_contents) |txt| { 144 | var cmake_dir = try util.zigCacheMakeOpenPath( 145 | self.builder, 146 | dir_name, 147 | .{}, 148 | .CmakeLists 149 | ); 150 | defer cmake_dir.close(); 151 | var cmakelists_txt = try cmake_dir.createFile(txt_name, .{}); 152 | try cmakelists_txt.writeAll(txt); 153 | std.log.scoped(.CMakeLists).info("Written CMakeLists.txt", .{}); 154 | 155 | self.build_dir = try util.zigCacheMakePath( 156 | self.builder, 157 | build_dir_name, 158 | .CmakeLists, 159 | ); 160 | 161 | self.path = try std.mem.concat(self.builder.allocator, u8, &.{ 162 | self.builder.build_root, 163 | std.fs.path.sep_str, 164 | self.builder.cache_root, 165 | std.fs.path.sep_str, 166 | dir_name, 167 | // std.fs.path.sep_str, 168 | // txt_name, 169 | }); 170 | } 171 | else return error.TxtContentsNull; 172 | } 173 | }; 174 | 175 | -------------------------------------------------------------------------------- /src/entry.c: -------------------------------------------------------------------------------- 1 | // C entry point which defined the main function. 2 | // Figure out how to change entry point to just have the 'main' definable in zig. 3 | extern void init(); 4 | extern void loop(); 5 | 6 | int main() { 7 | init(); 8 | while (1) loop(); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/pico.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const sdk = @import("sdk.zig"); 3 | const cmake = @import("cmake.zig"); 4 | const util = @import("util.zig"); 5 | const Library = @import("Library.zig"); 6 | 7 | const Builder = std.build.Builder; 8 | const Step = std.build.Step; 9 | const LibExeObjStep = std.build.LibExeObjStep; 10 | const CreateOptions = std.build.InstallRawStep.CreateOptions; 11 | 12 | pub const Board = union(enum) { 13 | pico: void, 14 | pico_w: struct { 15 | cyw43_arch: enum { 16 | threadsafe_background, 17 | poll, 18 | }, 19 | }, 20 | }; 21 | 22 | pub const rp2040_target = std.zig.CrossTarget { 23 | .cpu_arch = .thumb, 24 | .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m0plus }, 25 | .os_tag = .freestanding, 26 | .abi = .eabi, 27 | }; 28 | 29 | pub const PicoAppStep = struct { 30 | const Self = @This(); 31 | 32 | builder: *Builder, 33 | step: Step, 34 | zig: *LibExeObjStep, 35 | genpicolists: *sdk.GenPicoListsStep, 36 | cmakelists: *cmake.ListsStep, 37 | cmakebuild: *cmake.BuildStep, 38 | cmakemake: *cmake.MakeStep, 39 | emit_uf2: bool, 40 | 41 | pub fn create( 42 | builder: *Builder, 43 | name: []const u8, 44 | root_src: ?[]const u8, 45 | board: Board, 46 | libs: []const Library, 47 | ) *Self { 48 | const self = builder.allocator.create(Self) catch unreachable; 49 | 50 | const zig = builder.addStaticLibrary(name, root_src); 51 | zig.setTarget(rp2040_target); 52 | zig.override_dest_dir = std.build.InstallDir { 53 | .custom = "lib", 54 | }; 55 | 56 | const genpicolists = sdk.GenPicoListsStep.create( 57 | builder, 58 | zig, 59 | @tagName(board), 60 | libs, 61 | ); 62 | 63 | const cmakelists = cmake.ListsStep.create(builder); 64 | cmakelists.txt_src = &genpicolists.txt; 65 | cmakelists.step.dependOn(&genpicolists.step); 66 | 67 | _ = Library.IncludeStep.create( 68 | builder, 69 | libs, 70 | zig, 71 | cmakelists, 72 | board, 73 | ); 74 | 75 | const cmakebuild = cmake.BuildStep.create( 76 | builder, 77 | cmakelists, 78 | zig, 79 | ); 80 | 81 | const cmakemake = cmake.MakeStep.create(builder, cmakebuild); 82 | self.* = Self { 83 | .builder = builder, 84 | .step = Step.init( 85 | .custom, 86 | "picoapp-step", 87 | builder.allocator, 88 | make, 89 | ), 90 | .zig = zig, 91 | .genpicolists = genpicolists, 92 | .cmakelists = cmakelists, 93 | .cmakebuild = cmakebuild, 94 | .cmakemake = cmakemake, 95 | .emit_uf2 = true, 96 | }; 97 | 98 | self.zig.install(); 99 | self.step.dependOn(&cmakemake.step); 100 | self.cmakemake.step.dependOn(&self.zig.install_step.?.step); 101 | return self; 102 | } 103 | 104 | fn make(step: *Step) !void { 105 | const self = @fieldParentPtr(Self, "step", step); 106 | var build_dir = try std.fs.openDirAbsolute(self.cmakemake.build_dir.?, .{}); 107 | defer build_dir.close(); 108 | if (self.emit_uf2) { 109 | const uf2_name = try std.mem.concat(self.builder.allocator, u8, &.{ 110 | self.zig.name, ".uf2" 111 | }); 112 | var uf2_dir = try util.zigBuildMakeOpenPath( 113 | self.builder, 114 | "uf2", 115 | .{}, 116 | .PicoApp 117 | ); 118 | defer uf2_dir.close(); 119 | try build_dir.copyFile(uf2_name, uf2_dir, uf2_name, .{}); 120 | } 121 | } 122 | 123 | pub fn enable_stdio(self: *Self, stdio: sdk.Stdio_Options) void { 124 | self.genpicolists.enable_stdio = stdio; 125 | } 126 | 127 | pub fn install(self: *Self) void { 128 | self.builder.getInstallStep().dependOn(&self.step); 129 | } 130 | }; 131 | -------------------------------------------------------------------------------- /src/sdk.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const util = @import("util.zig"); 3 | const Library = @import("Library.zig"); 4 | 5 | const Builder = std.build.Builder; 6 | const Step = std.build.Step; 7 | const LibExeObjStep = std.build.LibExeObjStep; 8 | 9 | pub const GenPicoListsStep = struct { 10 | const Self = @This(); 11 | 12 | const entry_c = "entry.c"; 13 | const cmake_version = "3.9"; 14 | const c_std = "11"; 15 | const cxx_std = "17"; 16 | 17 | builder: *Builder, 18 | step: Step, 19 | txt: ?[]const u8, 20 | app: *LibExeObjStep, 21 | libs: []const Library, 22 | board: []const u8, 23 | enable_stdio: Stdio_Options, 24 | 25 | pub fn create( 26 | builder: *Builder, 27 | app: *LibExeObjStep, 28 | board: []const u8, 29 | libs: []const Library, 30 | ) *Self { 31 | const self = builder.allocator.create(Self) catch unreachable; 32 | self.* = Self { 33 | .builder = builder, 34 | .step = Step.init( 35 | .custom, 36 | "genpicolists-step", 37 | builder.allocator, 38 | make, 39 | ), 40 | .txt = null, 41 | .app = app, 42 | .libs = libs, 43 | .board = board, 44 | .enable_stdio = .none, 45 | }; 46 | return self; 47 | } 48 | 49 | fn make(step: *Step) !void { 50 | const self = @fieldParentPtr(Self, "step", step); 51 | const allocator = self.builder.allocator; 52 | 53 | var txt = std.ArrayList(u8).init(allocator); 54 | const writer = txt.writer(); 55 | 56 | const pico_sdk_import = try includePicoSdk(allocator); 57 | defer allocator.free(pico_sdk_import); 58 | 59 | const entry_c_path = try std.mem.concat(allocator, u8, &.{ 60 | util.picoZigDirPath(), 61 | std.fs.path.sep_str, 62 | entry_c, 63 | }); 64 | defer allocator.free(entry_c_path); 65 | 66 | const real_prefix_path = try util.zigInstallDirPath(self.builder); 67 | defer allocator.free(real_prefix_path); 68 | 69 | const app_path = try std.mem.concat(allocator, u8 , &.{ 70 | real_prefix_path, 71 | std.fs.path.sep_str, 72 | self.app.override_dest_dir.?.custom, 73 | std.fs.path.sep_str, 74 | "lib", 75 | self.app.name, 76 | ".a", 77 | }); 78 | defer allocator.free(app_path); 79 | 80 | const libnames = try Library.listNames(allocator, self.libs); 81 | defer allocator.free(libnames); 82 | 83 | const usb : i32 = 84 | if (self.enable_stdio == .usb or self.enable_stdio == .uart_usb) 1 else 0; 85 | const uart : i32 = 86 | if (self.enable_stdio == .uart or self.enable_stdio == .uart_usb) 1 else 0; 87 | 88 | try writer.print( 89 | \\cmake_minimum_required(VERSION {s}) 90 | \\set(PICO_BOARD "{s}") 91 | \\set(CMAKE_C_STANDARD {s}) 92 | \\set(CMAKE_CXX_STANDARD {s}) 93 | \\#include(pico_sdk_import.cmake) 94 | \\{s} 95 | \\project({s}) 96 | \\pico_sdk_init() 97 | \\add_library(zig-app-lib STATIC IMPORTED GLOBAL) 98 | \\set_target_properties(zig-app-lib PROPERTIES IMPORTED_LOCATION {s}) 99 | \\add_executable({s} {s}) 100 | \\target_link_libraries({s} zig-app-lib {s}) 101 | \\pico_enable_stdio_usb({s} {d}) 102 | \\pico_enable_stdio_uart({s} {d}) 103 | \\pico_add_extra_outputs({s}) 104 | , 105 | .{ 106 | cmake_version, 107 | self.board, 108 | c_std, 109 | cxx_std, 110 | pico_sdk_import, 111 | self.app.name, 112 | app_path, 113 | self.app.name, entry_c_path, 114 | self.app.name, libnames, 115 | self.app.name, usb, 116 | self.app.name, uart, 117 | self.app.name, 118 | } 119 | ); 120 | 121 | self.txt = txt.toOwnedSlice(); 122 | } 123 | 124 | fn includePicoSdk(allocator: std.mem.Allocator) ![]const u8 { 125 | if (util.picoSdkDirPath()) |pico_sdk_path| { 126 | var pico_sdk_dir = try std.fs.openDirAbsolute(pico_sdk_path, .{}); 127 | defer pico_sdk_dir.close(); 128 | var pico_sdk_import = try pico_sdk_dir.openFile( 129 | "external" ++ std.fs.path.sep_str ++ "pico_sdk_import.cmake", .{} 130 | ); 131 | defer pico_sdk_import.close(); 132 | var contents = std.ArrayList(u8).init(allocator); 133 | var reader = pico_sdk_import.reader(); 134 | try reader.readAllArrayList(&contents, 5000); 135 | return contents.toOwnedSlice(); 136 | } 137 | else |err| return err; 138 | } 139 | }; 140 | 141 | pub const Stdio_Options = enum { 142 | none, 143 | uart, 144 | usb, 145 | uart_usb 146 | }; 147 | 148 | -------------------------------------------------------------------------------- /src/util.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | /// Path to this src dir, required for 'entry.c' 4 | pub fn picoZigDirPath() []const u8 { 5 | return std.fs.path.dirname(@src().file) orelse unreachable; 6 | } 7 | 8 | pub fn picoSdkDirPath() ![]const u8 { 9 | return std.os.getenv("PICO_SDK_PATH") orelse error.PicoSdkPathEnv; 10 | } 11 | 12 | /// allocs with builder 13 | pub fn zigInstallDirPath(b : *std.build.Builder) ![]const u8 { 14 | var path_buffer : [std.fs.MAX_PATH_BYTES]u8 = .{}; 15 | if (std.fs.path.isAbsolute(b.install_prefix)) { 16 | return try b.allocator.dupe(u8, b.install_prefix); 17 | } 18 | else { 19 | const parent_dir = try std.fs.realpath( 20 | ".", 21 | &path_buffer 22 | ); 23 | return try std.mem.concat(b.allocator, u8, &.{ 24 | parent_dir, 25 | std.fs.path.sep_str, 26 | b.install_prefix, 27 | }); 28 | } 29 | } 30 | 31 | /// Like std.ChildProcess.exec but with failure based on process exit state 32 | pub fn exec( 33 | allocator: std.mem.Allocator, 34 | argv: []const []const u8, 35 | cwd: ?[]const u8, 36 | comptime log_scope: anytype, 37 | ) !void { 38 | const command = try std.mem.concat(allocator, u8, argv); 39 | defer allocator.free(command); 40 | 41 | std.log.scoped(log_scope).info("{s} $ {s}", .{cwd orelse "", command}); 42 | 43 | const result = try std.ChildProcess.exec(.{ 44 | .allocator = allocator, 45 | .argv = argv, 46 | .cwd = cwd, 47 | }); 48 | 49 | std.log.scoped(log_scope).info("{s}\n{s}", .{result.stderr, result.stdout}); 50 | 51 | switch (result.term) { 52 | .Exited => |code| { 53 | if (code != 0) return error.ProcessFailed; 54 | }, 55 | else => return error.ProcessUnhandled, 56 | } 57 | } 58 | 59 | pub fn zigCacheMakePath( 60 | b: *std.build.Builder, 61 | sub_path: []const u8, 62 | comptime log_scope: anytype, 63 | ) ![]const u8 { 64 | const actual_cache_root = b.pathFromRoot(b.cache_root); 65 | defer b.allocator.free(actual_cache_root); 66 | var cache_dir = try std.fs.openDirAbsolute(actual_cache_root, .{}); 67 | defer cache_dir.close(); 68 | try cache_dir.makePath(sub_path); 69 | std.log.scoped(log_scope).debug("made path {s}{s}{s}", .{ 70 | actual_cache_root, 71 | std.fs.path.sep_str, 72 | sub_path, 73 | }); 74 | return std.mem.concat(b.allocator, u8, &.{ 75 | actual_cache_root, 76 | std.fs.path.sep_str, 77 | sub_path, 78 | }); 79 | } 80 | 81 | /// Ensure path exists inside zig-cache and open it. 82 | pub fn zigCacheMakeOpenPath( 83 | b: *std.build.Builder, 84 | sub_path: []const u8, 85 | flags: std.fs.Dir.OpenDirOptions, 86 | comptime log_scope: anytype, 87 | ) !std.fs.Dir { 88 | const actual_cache_root = b.pathFromRoot(b.cache_root); 89 | defer b.allocator.free(actual_cache_root); 90 | var cache_dir = try std.fs.openDirAbsolute(actual_cache_root, .{}); 91 | defer cache_dir.close(); 92 | const dir = try cache_dir.makeOpenPath(sub_path, flags); 93 | std.log.scoped(log_scope).debug("opened path {s}{s}{s}", .{ 94 | actual_cache_root, 95 | std.fs.path.sep_str, 96 | sub_path, 97 | }); 98 | return dir; 99 | } 100 | 101 | pub fn zigBuildMakeOpenPath( 102 | b: *std.build.Builder, 103 | sub_path: []const u8, 104 | flags: std.fs.Dir.OpenDirOptions, 105 | comptime log_scope: anytype, 106 | ) !std.fs.Dir { 107 | const real_prefix_path = try zigInstallDirPath(b); 108 | defer b.allocator.free(real_prefix_path); 109 | var build_dir = try std.fs.openDirAbsolute(real_prefix_path, .{}); 110 | defer build_dir.close(); 111 | const dir = try build_dir.makeOpenPath(sub_path, flags); 112 | std.log.scoped(log_scope).debug("opened path {s}{s}{s}", .{ 113 | b.install_path, 114 | std.fs.path.sep_str, 115 | sub_path, 116 | }); 117 | return dir; 118 | } 119 | --------------------------------------------------------------------------------