forked from dirac-run/dirac
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild-proto.mjs
More file actions
executable file
·196 lines (176 loc) · 7 KB
/
build-proto.mjs
File metadata and controls
executable file
·196 lines (176 loc) · 7 KB
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
#!/usr/bin/env node
import chalk from "chalk"
import { execSync } from "child_process"
import * as fs from "fs/promises"
import { globby } from "globby"
import { createRequire } from "module"
import os from "os"
import * as path from "path"
import { rmrf } from "./file-utils.mjs"
import { main as generateHostBridgeClient } from "./generate-host-bridge-client.mjs"
import { main as generateProtoBusSetup } from "./generate-protobus-setup.mjs"
const require = createRequire(import.meta.url)
const PROTOC = path.join(require.resolve("grpc-tools"), "../bin/protoc")
const PROTO_DIR = path.resolve("proto")
const TS_OUT_DIR = path.resolve("src/shared/proto")
const GRPC_JS_OUT_DIR = path.resolve("src/generated/grpc-js")
const NICE_JS_OUT_DIR = path.resolve("src/generated/nice-grpc")
const DESCRIPTOR_OUT_DIR = path.resolve("dist-standalone/proto")
const isWindows = process.platform === "win32"
const TS_PROTO_PLUGIN = isWindows
? path.resolve("node_modules/.bin/protoc-gen-ts_proto.cmd") // Use the .bin directory path for Windows
: require.resolve("ts-proto/protoc-gen-ts_proto")
const TS_PROTO_OPTIONS = [
"env=both",
"esModuleInterop=true",
"outputServices=generic-definitions", // output generic ServiceDefinitions
"outputIndex=true", // output an index file for each package which exports all protos in the package.
"useOptionals=none", // scalar and message fields are required unless they are marked as optional.
"useDate=false", // Timestamp fields will not be automatically converted to Date.
]
async function main() {
await cleanup()
await compileProtos()
await generateProtoBusSetup()
await generateHostBridgeClient()
}
async function compileProtos() {
console.log(chalk.bold.blue("Compiling Protocol Buffers..."))
// Check for Apple Silicon compatibility before proceeding
checkAppleSiliconCompatibility()
// Create output directories if they don't exist
for (const dir of [TS_OUT_DIR, GRPC_JS_OUT_DIR, NICE_JS_OUT_DIR, DESCRIPTOR_OUT_DIR]) {
await fs.mkdir(dir, { recursive: true })
}
// Process all proto files
const protoFiles = await globby("**/*.proto", { cwd: PROTO_DIR, realpath: true })
console.log(chalk.cyan(`Processing ${protoFiles.length} proto files from`), PROTO_DIR)
tsProtoc(TS_OUT_DIR, protoFiles, TS_PROTO_OPTIONS)
// grpc-js is used to generate service impls for the ProtoBus service.
tsProtoc(GRPC_JS_OUT_DIR, protoFiles, ["outputServices=grpc-js", ...TS_PROTO_OPTIONS])
// nice-js is used for the Host Bridge client impls because it uses promises.
tsProtoc(NICE_JS_OUT_DIR, protoFiles, ["outputServices=nice-grpc,useExactTypes=false", ...TS_PROTO_OPTIONS])
const descriptorFile = path.join(DESCRIPTOR_OUT_DIR, "descriptor_set.pb")
const descriptorProtocCommand = [
PROTOC,
`--proto_path="${PROTO_DIR}"`,
`--descriptor_set_out="${descriptorFile}"`,
"--include_imports",
...protoFiles,
].join(" ")
try {
log_verbose(chalk.cyan("Generating descriptor set..."))
execSync(descriptorProtocCommand, { stdio: "inherit" })
} catch (error) {
console.error(chalk.red("Error generating descriptor set for proto file:"), error)
process.exit(1)
}
log_verbose(chalk.green("Protocol Buffer code generation completed successfully."))
log_verbose(chalk.green(`TypeScript files generated in: ${TS_OUT_DIR}`))
}
async function tsProtoc(outDir, protoFiles, protoOptions) {
// Build the protoc command with proper path handling for cross-platform
const command = [
PROTOC,
`--proto_path="${PROTO_DIR}"`,
`--plugin=protoc-gen-ts_proto="${TS_PROTO_PLUGIN}"`,
`--ts_proto_out="${outDir}"`,
`--ts_proto_opt=${protoOptions.join(",")} `,
...protoFiles.map((s) => `"${s}"`),
].join(" ")
try {
log_verbose(chalk.cyan(`Generating TypeScript code in ${outDir} for:\n${protoFiles.join("\n")}...`))
log_verbose(command)
execSync(command, { stdio: "inherit" })
} catch (error) {
console.error(chalk.red("Error generating TypeScript for proto files:"), error)
process.exit(1)
}
}
async function cleanup() {
// Clean up existing generated files
log_verbose(chalk.cyan("Cleaning up existing generated TypeScript files..."))
await rmrf(TS_OUT_DIR)
await rmrf("src/generated")
// Clean up generated files that were moved.
await rmrf("src/standalone/services/host-grpc-client.ts")
await rmrf("src/standalone/server-setup.ts")
await rmrf("src/hosts/vscode/host-grpc-service-config.ts")
await rmrf("src/core/controller/grpc-service-config.ts")
const oldhostbridgefiles = [
"src/hosts/vscode/workspace/methods.ts",
"src/hosts/vscode/workspace/index.ts",
"src/hosts/vscode/diff/methods.ts",
"src/hosts/vscode/diff/index.ts",
"src/hosts/vscode/env/methods.ts",
"src/hosts/vscode/env/index.ts",
"src/hosts/vscode/window/methods.ts",
"src/hosts/vscode/window/index.ts",
"src/hosts/vscode/watch/methods.ts",
"src/hosts/vscode/watch/index.ts",
"src/hosts/vscode/uri/methods.ts",
"src/hosts/vscode/uri/index.ts",
]
const oldprotobusfiles = [
"src/core/controller/account/index.ts",
"src/core/controller/account/methods.ts",
"src/core/controller/browser/index.ts",
"src/core/controller/browser/methods.ts",
"src/core/controller/checkpoints/index.ts",
"src/core/controller/checkpoints/methods.ts",
"src/core/controller/file/index.ts",
"src/core/controller/file/methods.ts",
"src/core/controller/models/index.ts",
"src/core/controller/models/methods.ts",
"src/core/controller/slash/index.ts",
"src/core/controller/slash/methods.ts",
"src/core/controller/state/index.ts",
"src/core/controller/state/methods.ts",
"src/core/controller/task/index.ts",
"src/core/controller/task/methods.ts",
"src/core/controller/ui/index.ts",
"src/core/controller/ui/methods.ts",
"src/core/controller/web/index.ts",
"src/core/controller/web/methods.ts",
]
for (const file of [...oldhostbridgefiles, ...oldprotobusfiles]) {
await rmrf(file)
}
}
// Check for Apple Silicon compatibility
function checkAppleSiliconCompatibility() {
// Only run check on macOS
if (process.platform !== "darwin") {
return
}
// Check if running on Apple Silicon
const cpuArchitecture = os.arch()
if (cpuArchitecture === "arm64") {
try {
// Check if Rosetta is installed
const rosettaCheck = execSync('/usr/bin/pgrep oahd || echo "NOT_INSTALLED"').toString().trim()
if (rosettaCheck === "NOT_INSTALLED") {
console.log(chalk.yellow("Detected Apple Silicon (ARM64) architecture."))
console.log(
chalk.red("Rosetta 2 is NOT installed. The npm version of protoc is not compatible with Apple Silicon."),
)
console.log(chalk.cyan("Please install Rosetta 2 using the following command:"))
console.log(chalk.cyan(" softwareupdate --install-rosetta --agree-to-license"))
console.log(chalk.red("Aborting build process."))
process.exit(1)
}
} catch (_error) {
console.log(chalk.yellow("Could not determine Rosetta installation status. Proceeding anyway."))
}
}
}
function log_verbose(s) {
if (process.argv.includes("-v") || process.argv.includes("--verbose")) {
console.log(s)
}
}
// Run the main function
main().catch((error) => {
console.error(chalk.red("Error:"), error)
process.exit(1)
})