1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
|
/******************************************************************************
__ ____ __
/ / ___ ____ _/ __/_____________ _/ /__
/ / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \
/ /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/
/_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/
(C)opyright 2025, Leafscale, LLC - https://www.leafscale.com
Project: Zygaena
Filename: ports.reef
Authors: Chris Tusa <chris.tusa@leafscale.com>
License: <see LICENSE file included with this source code>
Description: Ports command - port scaffolding and cache management
******************************************************************************/
module commands.ports
import sys.args
import io.file
import io.dir
import io.path
import core.str
import core.config
import core.portindex
import types
import util.color
import exitcodes as ec
import core.result as res
export
fn execute(opts: types.GlobalOptions): int
end export
fn execute(opts: types.GlobalOptions): int
let argc = args.count()
if argc <= opts.cmd_index + 1
print_usage()
return ec.EXIT_USAGE()
end if
let subcommand = args.get(opts.cmd_index + 1)
if subcommand == "new"
return ports_new(opts)
elif subcommand == "cache"
return ports_cache(opts)
else
color.print_error("Unknown subcommand: " + subcommand)
print_usage()
return ec.EXIT_USAGE()
end if
end execute
// ============================================================================
// coral ports new <category/name>
// ============================================================================
fn ports_new(opts: types.GlobalOptions): int
let argc = args.count()
if argc <= opts.cmd_index + 2
color.print_error("Missing argument: category/name")
println("Usage: coral ports new <category/name>")
return ec.EXIT_USAGE()
end if
let arg = args.get(opts.cmd_index + 2)
// Require slash format
let slash_idx = str.index_of(arg, "/")
if slash_idx <= 0
color.print_error("Invalid format: '" + arg + "'")
println("Use category/name format, e.g.: coral ports new hammerhead/my-port")
return ec.EXIT_USAGE()
end if
let category = str.substring(arg, 0, slash_idx)
let name = str.substring(arg, slash_idx + 1, str.length(arg) - slash_idx - 1)
if str.length(name) == 0
color.print_error("Port name cannot be empty")
return ec.EXIT_USAGE()
end if
// Get ports directory
let cfg = config.load()
let ports_dir = config.get_ports_dir(cfg)
// Create port directory
let port_dir = path.join_path(ports_dir, path.join_path(category, name))
if dir.dir_exists(port_dir)
color.print_error("Port directory already exists: " + port_dir)
return ec.EXIT_PKG_ALREADY_INSTALLED()
end if
if not res.is_ok(dir.create_dir_all(port_dir))
color.print_error("Failed to create directory: " + port_dir)
return ec.EXIT_PERMISSION_DENIED()
end if
// Generate package.toml
let toml_path = path.join_path(port_dir, "package.toml")
let toml_content = "[package]\nname = \"" + name + "\"\nversion = \"0.1.0\"\nrelease = 1\ndescription = \"\"\nurl = \"\"\nlicense = \"\"\nmaintainer = \"\"\narch = \"x86_64\"\n\n[dependencies]\nruntime = []\nbuild = []\n\n[[source]]\nfile = \"\"\nurls = []\nchecksum = \"SKIP\"\n\n[build]\nparallel = true\njobs = 0\n"
if not res.is_ok(file.writeFile(toml_path, toml_content))
color.print_error("Failed to write package.toml")
return ec.EXIT_PERMISSION_DENIED()
end if
// Generate build.sh
let build_path = path.join_path(port_dir, "build.sh")
let dollar = "$"
let build_content = "#!/bin/sh\n# Build script for " + name + "\nset -e\n\n# cd " + name + "-" + dollar + "{VERSION}\n\nMAKE=gmake\nif [ \"" + dollar + "(uname -s)\" != \"SunOS\" ]; then\n MAKE=make\nfi\n\n# ./configure --prefix=/usr\n# " + dollar + "MAKE " + dollar + "{MAKEFLAGS}\n# " + dollar + "MAKE DESTDIR=\"" + dollar + "{DESTDIR}\" install\n"
if not res.is_ok(file.writeFile(build_path, build_content))
color.print_error("Failed to write build.sh")
return ec.EXIT_PERMISSION_DENIED()
end if
color.print_success("Port created: " + category + "/" + name)
println("")
println(" Directory: " + port_dir)
println(" package.toml: " + toml_path)
println(" build.sh: " + build_path)
println("")
color.print_info("Next steps:")
println(" 1. Edit package.toml with package details")
println(" 2. Add source URLs and checksums")
println(" 3. Write the build.sh script")
println(" 4. Test with: coral build " + category + "/" + name)
return ec.EXIT_SUCCESS()
end ports_new
// ============================================================================
// coral ports cache [status|rebuild]
// ============================================================================
fn ports_cache(opts: types.GlobalOptions): int
let argc = args.count()
if argc <= opts.cmd_index + 2
// Default to status
return cache_status()
end if
let subcmd = args.get(opts.cmd_index + 2)
if subcmd == "status"
return cache_status()
elif subcmd == "rebuild"
return cache_rebuild()
else
color.print_error("Unknown cache subcommand: " + subcmd)
println("Usage: coral ports cache [status|rebuild]")
return ec.EXIT_USAGE()
end if
end ports_cache
fn cache_status(): int
color.print_action("Ports index status:")
println("")
if portindex.port_index_exists()
let count = portindex.get_port_count()
println(" Status: built")
println(" Ports indexed: " + int_to_str(count))
else
println(" Status: not built")
color.print_info("Run 'coral ports cache rebuild' to build the index")
end if
return ec.EXIT_SUCCESS()
end cache_status
fn cache_rebuild(): int
color.print_action("Rebuilding ports index...")
println("")
if portindex.rebuild_port_index()
let count = portindex.get_port_count()
color.print_success("Ports index rebuilt successfully")
println(" Ports indexed: " + int_to_str(count))
return ec.EXIT_SUCCESS()
else
color.print_error("Failed to rebuild ports index")
return ec.EXIT_DB_ERROR()
end if
end cache_rebuild
// ============================================================================
// Usage
// ============================================================================
proc print_usage()
println("Usage: coral ports <command> [args]")
println("")
println("Port development and cache management.")
println("")
println("Commands:")
println(" new <category/name> Scaffold a new port")
println(" cache [status] Show ports index status")
println(" cache rebuild Rebuild ports index")
println("")
println("Examples:")
println(" coral ports new hammerhead/my-port")
println(" coral ports cache status")
println(" coral ports cache rebuild")
end print_usage
// Helper: convert int to string
fn int_to_str(n: int): string
if n == 0
return "0"
end if
mut negative = false
mut value = n
if n < 0
negative = true
value = 0 - n
end if
mut result = ""
while value > 0
let digit = value % 10
result = str.concat(str.substring("0123456789", digit, 1), result)
value = value / 10
end while
if negative
result = str.concat("-", result)
end if
return result
end int_to_str
end module
|