Skip to content

Commit 3c7bfce

Browse files
committed
support for program name/version; fixes microsoft#170
1 parent 6e8d2ce commit 3c7bfce

11 files changed

Lines changed: 174 additions & 19 deletions

File tree

cli/src/build.ts

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ import type { DevsModule } from "@devicescript/vm"
3838
import { readFile, writeFile } from "node:fs/promises"
3939
import { printDmesg } from "./vmworker"
4040
import { EXIT_CODE_COMPILATION_ERROR } from "./exitcodes"
41-
import { converters, parseServiceSpecificationMarkdownToJSON } from "jacdac-ts"
41+
import {
42+
converters,
43+
parseServiceSpecificationMarkdownToJSON,
44+
versionTryParse,
45+
} from "jacdac-ts"
46+
import { execSync } from "node:child_process"
4247

4348
// TODO should we move this to jacdac-ts and call automatically for transports?
4449
export function setupWebsocket() {
@@ -198,6 +203,68 @@ function toDevsDiag(d: jdspec.Diagnostic): DevsDiagnostic {
198203
}
199204
}
200205

206+
function execCmd(cmd: string) {
207+
try {
208+
return execSync(cmd, { encoding: "utf-8" }).trim()
209+
} catch {
210+
return ""
211+
}
212+
}
213+
214+
function compilePackageJson(
215+
tsdir: string,
216+
entryPoint: string,
217+
lcfg: LocalBuildConfig,
218+
errors: DevsDiagnostic[]
219+
) {
220+
const pkgJsonPath = join(tsdir, "package.json")
221+
if (existsSync(pkgJsonPath)) {
222+
const pkgJSON = JSON.parse(readFileSync(pkgJsonPath, "utf-8"))
223+
lcfg.hwInfo.progName = pkgJSON.name ?? "(no name)"
224+
lcfg.hwInfo.progVersion = pkgJSON.version ?? "(no version)"
225+
const head = execCmd("git describe --tags --match 'v[0-9]*' --always")
226+
let dirty = execCmd(
227+
"git status --porcelain --untracked-file=no --ignore-submodules=untracked"
228+
)
229+
if (!head) dirty = "yes"
230+
const exact = !dirty && head[0] == "v" && !head.includes("-")
231+
if (exact) {
232+
lcfg.hwInfo.progVersion = head
233+
} else {
234+
let v = versionTryParse(lcfg.hwInfo.progVersion)
235+
if (head[0] == "v") v = versionTryParse(head) || v
236+
let verStr = ""
237+
if (v) verStr = `v${v.major}.${v.minor}.${v.patch + 1}-`
238+
verStr += head.replace(/.*-/, "")
239+
if (dirty) {
240+
const now = new Date()
241+
.toISOString()
242+
.replace(/T/, ".")
243+
.replace(/:/, ".")
244+
.replace(/:.*/, "")
245+
.replace(/-/g, ".")
246+
if (verStr) verStr += "-"
247+
verStr += now
248+
}
249+
lcfg.hwInfo.progVersion = verStr
250+
}
251+
}
252+
253+
if (entryPoint) {
254+
entryPoint = entryPoint.replace(/^src[\/\\]/, "")
255+
entryPoint = entryPoint.replace(/^main/, "")
256+
entryPoint = entryPoint.replace(/.ts$/, "")
257+
if (entryPoint) {
258+
if (lcfg.hwInfo.progName) lcfg.hwInfo.progName += " " + entryPoint
259+
else lcfg.hwInfo.progName += entryPoint
260+
}
261+
}
262+
263+
verboseLog(
264+
`compile: ${lcfg.hwInfo.progName} ${lcfg.hwInfo.progVersion ?? ""}`
265+
)
266+
}
267+
201268
function compileServiceSpecs(
202269
tsdir: string,
203270
lcfg: LocalBuildConfig,
@@ -295,11 +362,19 @@ export class CompilationError extends Error {
295362
}
296363
}
297364

298-
export function buildConfigFromDir(dir: string, options: BuildOptions = {}) {
299-
const lcfg: LocalBuildConfig = {}
365+
export function buildConfigFromDir(
366+
dir: string,
367+
entryPoint: string = "",
368+
options: BuildOptions = {}
369+
) {
370+
const lcfg: LocalBuildConfig = {
371+
hwInfo: {},
372+
}
300373
const errors: DevsDiagnostic[] = []
301374

302375
if (dir) {
376+
verboseLog(`build config from: ${dir}`)
377+
compilePackageJson(dir, entryPoint, lcfg, errors)
303378
compileServiceSpecs(dir, lcfg, errors)
304379
compileBoards(dir, lcfg, errors)
305380
if (!options.quiet)
@@ -330,7 +405,12 @@ export async function compileFile(
330405
ensureDirSync(options.outDir || BINDIR)
331406

332407
const folder = resolve(".")
333-
const { errors, buildConfig } = buildConfigFromDir(folder, options)
408+
const entryPoint = relative(folder, fn)
409+
const { errors, buildConfig } = buildConfigFromDir(
410+
folder,
411+
entryPoint,
412+
options
413+
)
334414
const host = await getHost(buildConfig, options, folder)
335415

336416
const res = compileWithHost(fn, host)

cli/src/ctool.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,20 @@ export async function ctool(options: CToolOptions) {
3636

3737
if (options.empty) {
3838
const host = await getHost(
39-
resolveBuildConfig(),
39+
resolveBuildConfig({
40+
hwInfo: {
41+
// This doesn't work anyway, since settings are not fetched from the empty program
42+
// progName: "(empty)",
43+
// progVersion: `v${BinFmt.IMG_VERSION_MAJOR}.${BinFmt.IMG_VERSION_MINOR}.${BinFmt.IMG_VERSION_PATCH}`,
44+
},
45+
}),
4046
{ verify: false },
4147
"."
4248
)
43-
host.read = () => ""
49+
host.read = fn => {
50+
if (fn.includes("package.json")) throw new Error("read empty")
51+
return ""
52+
}
4453
const res = compileWithHost("src/main.ts", host)
4554
const buf = res.binary
4655
let r = `__attribute__((aligned(sizeof(void *)))) static const uint8_t devs_empty_program[${buf.length}] = {`

compiler/src/compiler.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ import {
5959
mkDiag,
6060
trace,
6161
} from "./tsiface"
62-
import { preludeFiles, resolveBuildConfig } from "./specgen"
62+
import {
63+
preludeFiles,
64+
resolveBuildConfig,
65+
unresolveBuildConfig,
66+
} from "./specgen"
6367
import {
6468
VarDebugInfo,
6569
RoleDebugInfo,
@@ -75,6 +79,7 @@ import {
7579
ResolvedBuildConfig,
7680
SystemReg,
7781
SRV_DEVICE_SCRIPT_CONDITION,
82+
ProgramConfig,
7883
} from "@devicescript/interop"
7984
import { BaseServiceConfig } from "@devicescript/srvcfg"
8085
import { jsonToDcfg, serializeDcfg } from "./dcfg"
@@ -3630,12 +3635,15 @@ class Program implements TopOpWriter {
36303635
}
36313636

36323637
private serializeStartServices() {
3638+
const bcfg = this.host.getConfig()
3639+
const jcfg: ProgramConfig = bcfg?.hwInfo ?? {}
36333640
const cfg = jsonToDcfg({
3641+
...jcfg,
36343642
services: new Array(0x40).concat(this.startServices),
36353643
})
36363644
const bin = serializeDcfg(cfg)
36373645
const writer = new SectionWriter()
3638-
if (this.startServices.length) {
3646+
if (this.startServices.length || Object.keys(jcfg).length) {
36393647
writer.append(bin)
36403648
writer.align()
36413649
}
@@ -3914,6 +3922,7 @@ class Program implements TopOpWriter {
39143922
roles: roleData.size,
39153923
align: left,
39163924
},
3925+
localConfig: unresolveBuildConfig(this.host.getConfig()),
39173926
roles: this.roles.map(r => r.debugInfo()),
39183927
functions: this.procs.map(p => p.debugInfo()),
39193928
globals: this.globals.map(r => r.debugInfo()),

compiler/src/specgen.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,27 @@ const REGISTER_STRING = "Register<string>"
3535
const REGISTER_BUFFER = "Register<Buffer>"
3636
const REGISTER_ARRAY = "Register<any[]>"
3737

38+
export function unresolveBuildConfig(
39+
cfg: ResolvedBuildConfig
40+
): LocalBuildConfig {
41+
if (!cfg) cfg = {} as any
42+
const r: LocalBuildConfig = {
43+
hwInfo: cfg.hwInfo,
44+
addArchs: cfg.addArchs,
45+
addBoards: cfg.addBoards,
46+
addServices: cfg.addServices,
47+
}
48+
return JSON.parse(JSON.stringify(r))
49+
}
50+
3851
export function resolveBuildConfig(
3952
local?: LocalBuildConfig
4053
): ResolvedBuildConfig {
4154
const r: ResolvedBuildConfig = {
55+
hwInfo: Object.assign({}, local?.hwInfo ?? {}),
56+
addArchs: local?.addArchs,
57+
addBoards: local?.addBoards,
58+
addServices: local?.addServices,
4259
boards: Object.assign({}, boardSpecifications.boards),
4360
archs: Object.assign({}, boardSpecifications.archs),
4461
services: jacdacDefaultSpecifications.concat(local?.addServices ?? []),

interop/src/archconfig.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,24 @@ import {
99
ServiceConfig,
1010
} from "@devicescript/srvcfg"
1111

12-
export interface DeviceConfig extends DeviceHardwareInfo, JsonComment {
12+
export interface DeviceProps {
13+
/**
14+
* Name of the program, derived from package.json. Exposed as `program_name` register.
15+
*/
16+
progName?: string
17+
18+
/**
19+
* Version number of the program, derived from package.json and git. Exposed as `program_version` register.
20+
*/
21+
progVersion?: string
22+
}
23+
24+
export type ProgramConfig = Partial<DeviceProps> & Partial<DeviceHardwareInfo>
25+
26+
export interface DeviceConfig
27+
extends DeviceProps,
28+
DeviceHardwareInfo,
29+
JsonComment {
1330
$schema?: string
1431

1532
/**
@@ -92,13 +109,19 @@ export function normalizeDeviceConfig(
92109
return res
93110
}
94111

112+
export interface ProgramBuildConfig {
113+
name?: string
114+
version?: string
115+
}
116+
95117
export interface LocalBuildConfig {
118+
hwInfo: ProgramConfig
96119
addBoards?: DeviceConfig[]
97120
addArchs?: ArchConfig[]
98121
addServices?: jdspec.ServiceSpec[]
99122
}
100123

101-
export interface ResolvedBuildConfig {
124+
export interface ResolvedBuildConfig extends LocalBuildConfig {
102125
boards: Record<string, DeviceConfig>
103126
archs: Record<string, ArchConfig>
104127
services: jdspec.ServiceSpec[]

interop/src/info.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
// location is offset into concatenation of all SrcFile's plus length
2+
3+
import { LocalBuildConfig } from "./archconfig"
4+
25
// both are in 16-bit JS codepoints
36
export type SrcLocation = [number, number]
47
export interface SrcFile {
@@ -51,6 +54,7 @@ export interface DebugInfo {
5154
roles: number
5255
align: number
5356
}
57+
localConfig: LocalBuildConfig
5458
functions: FunctionDebugInfo[]
5559
roles: RoleDebugInfo[]
5660
globals: VarDebugInfo[]
@@ -76,6 +80,9 @@ export function emptyDebugInfo(): DebugInfo {
7680
roles: 0,
7781
align: 0,
7882
},
83+
localConfig: {
84+
hwInfo: {},
85+
},
7986
functions: [],
8087
globals: [],
8188
roles: [],

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"name": "DeviceScript-workspace",
23
"version": "0.0.0",
34
"private": true,
45
"scripts": {

runtime/devicescript/devsmgr.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,17 @@ static void hash_program(srv_t *state, jd_packet_t *pkt) {
343343
jd_send(pkt->service_index, pkt->service_command, hash, JD_SHA256_HASH_BYTES);
344344
}
345345

346+
static bool respond_dcfg(jd_packet_t *pkt, unsigned reg, const char *setting) {
347+
if (pkt->service_command == JD_GET(reg)) {
348+
const char *res = dcfg_get_string(setting, NULL);
349+
if (!res)
350+
res = "";
351+
jd_respond_string(pkt, res);
352+
return true;
353+
}
354+
return false;
355+
}
356+
346357
void devsmgr_handle_packet(srv_t *state, jd_packet_t *pkt) {
347358
switch (pkt->service_command) {
348359
case JD_DEVICE_SCRIPT_MANAGER_CMD_DEPLOY_BYTECODE:
@@ -374,6 +385,10 @@ void devsmgr_handle_packet(srv_t *state, jd_packet_t *pkt) {
374385
break;
375386

376387
default:
388+
if (respond_dcfg(pkt, JD_DEVICE_SCRIPT_MANAGER_REG_PROGRAM_VERSION, "progVersion") ||
389+
respond_dcfg(pkt, JD_DEVICE_SCRIPT_MANAGER_REG_PROGRAM_NAME, "progName"))
390+
break;
391+
377392
switch (service_handle_register_final(state, pkt, devsmgr_regs)) {
378393
case JD_DEVICE_SCRIPT_MANAGER_REG_RUNNING:
379394
if (state->running && !state->ctx) {

runtime/jacdac-c

Submodule jacdac-c updated 1 file

vscode/src/devtoolsserver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ export class DeveloperToolsManager extends JDEventSource {
594594
if (isWindows) {
595595
cli = "node_modules\\.bin\\devicescript.cmd"
596596
} else args.unshift("./node_modules/.bin/devicescript")
597-
if (diagnostics) args.push("--diagnostics")
597+
if (diagnostics) args.push("--diagnostics", "--verbose")
598598
console.debug(
599599
`create terminal: ${useShell ? "shell:" : ""}${
600600
cwd.fsPath

0 commit comments

Comments
 (0)