libdragon 10.0.0 → 10.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +102 -3
- package/README.md +9 -39
- package/index.js +38 -32
- package/modules/actions.js +238 -129
- package/modules/constants.js +1 -0
- package/modules/helpers.js +155 -69
- package/modules/usage.js +129 -0
- package/package.json +5 -3
- package/skeleton/Makefile +18 -0
- package/skeleton/src/main.c +15 -0
package/modules/helpers.js
CHANGED
|
@@ -9,6 +9,7 @@ const {
|
|
|
9
9
|
LIBDRAGON_PROJECT_MANIFEST,
|
|
10
10
|
IMAGE_FILE,
|
|
11
11
|
DOCKER_HUB_IMAGE,
|
|
12
|
+
CONTAINER_TARGET_PATH,
|
|
12
13
|
} = require('./constants');
|
|
13
14
|
|
|
14
15
|
const globals = {
|
|
@@ -16,16 +17,20 @@ const globals = {
|
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
class CommandError extends Error {
|
|
19
|
-
constructor(message, { code, out,
|
|
20
|
+
constructor(message, { code, out, userCommand }) {
|
|
20
21
|
super(message);
|
|
21
22
|
this.code = code;
|
|
22
23
|
this.out = out;
|
|
23
|
-
this.
|
|
24
|
+
this.userCommand = userCommand;
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
// A simple Promise wrapper for child_process.spawn
|
|
28
|
-
|
|
28
|
+
// A simple Promise wrapper for child_process.spawn. If interactive is true,
|
|
29
|
+
// stdout becomes a tty when available and we cannot read the stdout from the
|
|
30
|
+
// command anymore. If interactive is "full", the error stream is also piped to
|
|
31
|
+
// the main process as TTY, so it is not readable anymore as well. For the
|
|
32
|
+
// commands using stderr as TTY it should be set to "full"
|
|
33
|
+
function spawnProcess(cmd, params = [], userCommand, interactive = false) {
|
|
29
34
|
return new Promise((resolve, reject) => {
|
|
30
35
|
let stdout = [];
|
|
31
36
|
let stderr = [];
|
|
@@ -34,21 +39,41 @@ function spawnProcess(cmd, params = [], showOutput) {
|
|
|
34
39
|
log(chalk.grey(`Spawning: ${cmd} ${params.join(' ')}`), true);
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const isTTY =
|
|
43
|
+
process.stdin.isTTY && process.stdout.isTTY && process.stderr.isTTY;
|
|
44
|
+
const enableTTY = isTTY && !!interactive;
|
|
45
|
+
const enableErrorTTY = isTTY && interactive === 'full';
|
|
46
|
+
|
|
47
|
+
const command = spawn(cmd, params, {
|
|
48
|
+
// We should redirect streams together for the TTY to work
|
|
49
|
+
// properly if they are all used as TTY
|
|
50
|
+
stdio: [
|
|
51
|
+
enableTTY ? 'inherit' : 'pipe',
|
|
52
|
+
enableTTY ? 'inherit' : 'pipe',
|
|
53
|
+
enableErrorTTY ? 'inherit' : 'pipe',
|
|
54
|
+
],
|
|
44
55
|
});
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
57
|
+
if (!enableTTY && (globals.verbose || userCommand)) {
|
|
58
|
+
command.stdout.pipe(process.stdout);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!enableErrorTTY && (globals.verbose || userCommand)) {
|
|
62
|
+
command.stderr.pipe(process.stderr);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// We shouldn't need to collect the data if it is a user command.
|
|
66
|
+
if (!enableTTY && !userCommand) {
|
|
67
|
+
command.stdout.on('data', function (data) {
|
|
68
|
+
stdout.push(Buffer.from(data));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!enableErrorTTY) {
|
|
73
|
+
command.stderr.on('data', function (data) {
|
|
74
|
+
stderr.push(Buffer.from(data));
|
|
75
|
+
});
|
|
76
|
+
}
|
|
52
77
|
|
|
53
78
|
const errorHandler = (err) => {
|
|
54
79
|
command.off('close', closeHandler);
|
|
@@ -65,7 +90,7 @@ function spawnProcess(cmd, params = [], showOutput) {
|
|
|
65
90
|
{
|
|
66
91
|
code,
|
|
67
92
|
out: Buffer.concat(stderr).toString(),
|
|
68
|
-
|
|
93
|
+
userCommand,
|
|
69
94
|
}
|
|
70
95
|
);
|
|
71
96
|
reject(err);
|
|
@@ -77,29 +102,49 @@ function spawnProcess(cmd, params = [], showOutput) {
|
|
|
77
102
|
});
|
|
78
103
|
}
|
|
79
104
|
|
|
80
|
-
function dockerExec(
|
|
105
|
+
function dockerExec(
|
|
106
|
+
libdragonInfo,
|
|
107
|
+
dockerParams,
|
|
108
|
+
cmdWithParams,
|
|
109
|
+
userCommand,
|
|
110
|
+
interactive
|
|
111
|
+
) {
|
|
81
112
|
// TODO: assert for invalid args
|
|
82
113
|
const haveDockerParams =
|
|
83
114
|
Array.isArray(dockerParams) && Array.isArray(cmdWithParams);
|
|
115
|
+
const isTTY = process.stdin.isTTY && process.stdout.isTTY;
|
|
116
|
+
// interactive and TTY?
|
|
117
|
+
const additionalParams =
|
|
118
|
+
isTTY && (haveDockerParams ? interactive : userCommand) ? ['-it'] : [];
|
|
84
119
|
return spawnProcess(
|
|
85
120
|
'docker',
|
|
86
121
|
[
|
|
87
122
|
'exec',
|
|
88
|
-
...(haveDockerParams
|
|
123
|
+
...(haveDockerParams
|
|
124
|
+
? [...dockerParams, ...additionalParams]
|
|
125
|
+
: additionalParams),
|
|
89
126
|
libdragonInfo.containerId,
|
|
90
127
|
...(haveDockerParams ? cmdWithParams : dockerParams),
|
|
91
128
|
],
|
|
92
|
-
haveDockerParams ?
|
|
129
|
+
haveDockerParams ? userCommand : cmdWithParams,
|
|
130
|
+
haveDockerParams ? interactive : userCommand
|
|
93
131
|
);
|
|
94
132
|
}
|
|
95
133
|
|
|
96
134
|
/**
|
|
97
135
|
* Invokes host git with provided params. If host does not have git, falls back
|
|
98
|
-
* to the docker git, with the user set to the user running libdragon.
|
|
136
|
+
* to the docker git, with the nix user set to the user running libdragon.
|
|
99
137
|
*/
|
|
100
|
-
async function runGitMaybeHost(libdragonInfo, params,
|
|
138
|
+
async function runGitMaybeHost(libdragonInfo, params, interactive = 'full') {
|
|
101
139
|
try {
|
|
102
|
-
return await spawnProcess(
|
|
140
|
+
return await spawnProcess(
|
|
141
|
+
'git',
|
|
142
|
+
params,
|
|
143
|
+
false,
|
|
144
|
+
// Windows git is breaking the TTY somehow - disable interactive for now
|
|
145
|
+
// We are not able to display progress for the initial clone b/c of this
|
|
146
|
+
/^win/.test(process.platform) ? false : interactive
|
|
147
|
+
);
|
|
103
148
|
} catch (e) {
|
|
104
149
|
if (!(e instanceof CommandError)) {
|
|
105
150
|
return await dockerExec(
|
|
@@ -107,7 +152,8 @@ async function runGitMaybeHost(libdragonInfo, params, showOutput) {
|
|
|
107
152
|
// Use the host user when initializing git as we will need access
|
|
108
153
|
[...dockerHostUserParams(libdragonInfo)],
|
|
109
154
|
['git', ...params],
|
|
110
|
-
|
|
155
|
+
false,
|
|
156
|
+
interactive
|
|
111
157
|
);
|
|
112
158
|
}
|
|
113
159
|
throw e;
|
|
@@ -148,16 +194,16 @@ async function findNPMRoot() {
|
|
|
148
194
|
}
|
|
149
195
|
}
|
|
150
196
|
|
|
151
|
-
function
|
|
152
|
-
return
|
|
153
|
-
|
|
154
|
-
'/
|
|
155
|
-
|
|
156
|
-
|
|
197
|
+
function dockerRelativeWorkdir(libdragonInfo) {
|
|
198
|
+
return (
|
|
199
|
+
CONTAINER_TARGET_PATH +
|
|
200
|
+
'/' +
|
|
201
|
+
toPosixPath(path.relative(libdragonInfo.root, process.cwd()))
|
|
202
|
+
);
|
|
157
203
|
}
|
|
158
204
|
|
|
159
|
-
function
|
|
160
|
-
return
|
|
205
|
+
function dockerRelativeWorkdirParams(libdragonInfo) {
|
|
206
|
+
return ['--workdir', dockerRelativeWorkdir(libdragonInfo)];
|
|
161
207
|
}
|
|
162
208
|
|
|
163
209
|
function dockerHostUserParams(libdragonInfo) {
|
|
@@ -174,6 +220,40 @@ async function findGitRoot() {
|
|
|
174
220
|
}
|
|
175
221
|
}
|
|
176
222
|
|
|
223
|
+
async function findContainerId(libdragonInfo) {
|
|
224
|
+
const idFile = path.join(libdragonInfo.root, '.git', CACHED_CONTAINER_FILE);
|
|
225
|
+
if (fs.existsSync(idFile)) {
|
|
226
|
+
const id = fs.readFileSync(idFile, { encoding: 'utf8' }).trim();
|
|
227
|
+
log(`Read containerId: ${id}`, true);
|
|
228
|
+
return id;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const candidates = (
|
|
232
|
+
await spawnProcess('docker', [
|
|
233
|
+
'container',
|
|
234
|
+
'ls',
|
|
235
|
+
'-a',
|
|
236
|
+
'--format',
|
|
237
|
+
'{{.}}{{.ID}}',
|
|
238
|
+
'-f',
|
|
239
|
+
'volume=' + CONTAINER_TARGET_PATH,
|
|
240
|
+
])
|
|
241
|
+
)
|
|
242
|
+
.split('\n')
|
|
243
|
+
.filter((s) => s.includes(`${libdragonInfo.root} `));
|
|
244
|
+
|
|
245
|
+
if (candidates.length > 0) {
|
|
246
|
+
const str = candidates[0];
|
|
247
|
+
const shortId = str.slice(-12);
|
|
248
|
+
const idIndex = str.indexOf(shortId);
|
|
249
|
+
const longId = str.slice(idIndex, idIndex + 64);
|
|
250
|
+
if (longId.length === 64) {
|
|
251
|
+
tryCacheContainerId({ ...libdragonInfo, containerId: longId });
|
|
252
|
+
return longId;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
177
257
|
async function checkContainerAndClean(libdragonInfo) {
|
|
178
258
|
const id =
|
|
179
259
|
libdragonInfo.containerId &&
|
|
@@ -224,62 +304,59 @@ async function readProjectInfo() {
|
|
|
224
304
|
if (!info.root) {
|
|
225
305
|
log('Could not find project root, set as cwd.', true);
|
|
226
306
|
info.root = process.cwd();
|
|
227
|
-
return info;
|
|
228
307
|
}
|
|
229
308
|
|
|
230
309
|
log(`Project root: ${info.root}`, true);
|
|
231
310
|
|
|
232
|
-
const idFile = path.join(info.root, '.git', CACHED_CONTAINER_FILE);
|
|
233
|
-
if (fs.existsSync(idFile)) {
|
|
234
|
-
const id = fs.readFileSync(idFile, { encoding: 'utf8' }).trim();
|
|
235
|
-
log(`Read containerId: ${id}`, true);
|
|
236
|
-
info.containerId = id;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
311
|
const imageFile = path.join(
|
|
240
312
|
info.root,
|
|
241
313
|
LIBDRAGON_PROJECT_MANIFEST,
|
|
242
314
|
IMAGE_FILE
|
|
243
315
|
);
|
|
244
316
|
|
|
317
|
+
// flag has the highest priority followed by the one read from the file
|
|
318
|
+
// and then if there is any matching container, name is read from it. As last
|
|
319
|
+
// option fallback to default value.
|
|
245
320
|
if (fs.existsSync(imageFile) && !fs.statSync(imageFile).isDirectory()) {
|
|
246
321
|
info.imageName = fs.readFileSync(imageFile, { encoding: 'utf8' }).trim();
|
|
247
322
|
}
|
|
248
323
|
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
async function updateImageName(libdragonInfo) {
|
|
253
|
-
const manifestPath = path.join(
|
|
254
|
-
libdragonInfo.root,
|
|
255
|
-
LIBDRAGON_PROJECT_MANIFEST
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
// flag has the highest priority followed by the one read from the file
|
|
259
|
-
// and then if there is any matching container, name is read from it. As last
|
|
260
|
-
// option fallback to default value.
|
|
261
|
-
let imageName = libdragonInfo.options.DOCKER_IMAGE ?? libdragonInfo.imageName;
|
|
324
|
+
info.containerId = await findContainerId(info);
|
|
325
|
+
log(`Active container id: ${info.containerId}`, true);
|
|
262
326
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
imageName = await spawnProcess('docker', [
|
|
327
|
+
// If still have the container, read the image name from it
|
|
328
|
+
if (!info.imageName && (await checkContainerAndClean(info))) {
|
|
329
|
+
info.imageName = (
|
|
330
|
+
await spawnProcess('docker', [
|
|
268
331
|
'container',
|
|
269
332
|
'inspect',
|
|
270
|
-
containerId,
|
|
333
|
+
info.containerId,
|
|
271
334
|
'--format',
|
|
272
335
|
'{{.Config.Image}}',
|
|
273
|
-
])
|
|
274
|
-
|
|
336
|
+
])
|
|
337
|
+
).trim();
|
|
338
|
+
|
|
339
|
+
// Cache the image name
|
|
340
|
+
await updateImageName(info);
|
|
275
341
|
}
|
|
276
342
|
|
|
277
|
-
imageName = imageName ?? DOCKER_HUB_IMAGE;
|
|
343
|
+
info.imageName = info.imageName ?? DOCKER_HUB_IMAGE;
|
|
344
|
+
log(`Active image name: ${info.imageName}`, true);
|
|
345
|
+
return info;
|
|
346
|
+
}
|
|
278
347
|
|
|
348
|
+
async function updateImageName(libdragonInfo) {
|
|
349
|
+
if (!libdragonInfo.imageName) return;
|
|
350
|
+
const manifestPath = path.join(
|
|
351
|
+
libdragonInfo.root,
|
|
352
|
+
LIBDRAGON_PROJECT_MANIFEST
|
|
353
|
+
);
|
|
279
354
|
await createManifestIfNotExist(libdragonInfo);
|
|
280
|
-
fs.writeFileSync(
|
|
281
|
-
|
|
282
|
-
|
|
355
|
+
fs.writeFileSync(
|
|
356
|
+
path.join(manifestPath, IMAGE_FILE),
|
|
357
|
+
libdragonInfo.imageName
|
|
358
|
+
);
|
|
359
|
+
log(`Image name updated: ${libdragonInfo.imageName}`, true);
|
|
283
360
|
}
|
|
284
361
|
|
|
285
362
|
/**
|
|
@@ -302,9 +379,17 @@ async function createManifestIfNotExist(libdragonInfo) {
|
|
|
302
379
|
`Creating libdragon project configuration at \`${libdragonInfo.root}\`.`
|
|
303
380
|
);
|
|
304
381
|
fs.mkdirSync(manifestPath);
|
|
305
|
-
return true;
|
|
306
382
|
}
|
|
307
|
-
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function tryCacheContainerId(libdragonInfo) {
|
|
386
|
+
const gitFolder = path.join(libdragonInfo.root, '.git');
|
|
387
|
+
if (fs.existsSync(gitFolder) && fs.statSync(gitFolder).isDirectory()) {
|
|
388
|
+
fs.writeFileSync(
|
|
389
|
+
path.join(libdragonInfo.root, '.git', CACHED_CONTAINER_FILE),
|
|
390
|
+
libdragonInfo.containerId
|
|
391
|
+
);
|
|
392
|
+
}
|
|
308
393
|
}
|
|
309
394
|
|
|
310
395
|
function toPosixPath(p) {
|
|
@@ -337,9 +422,10 @@ module.exports = {
|
|
|
337
422
|
log,
|
|
338
423
|
dockerExec,
|
|
339
424
|
dockerRelativeWorkdirParams,
|
|
340
|
-
dockerByteSwapParams,
|
|
341
425
|
runGitMaybeHost,
|
|
342
426
|
dockerHostUserParams,
|
|
427
|
+
dockerRelativeWorkdir,
|
|
428
|
+
tryCacheContainerId,
|
|
343
429
|
CommandError,
|
|
344
430
|
globals,
|
|
345
431
|
};
|
package/modules/usage.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const commandLineUsage = require('command-line-usage');
|
|
3
|
+
|
|
4
|
+
const { log } = require('./helpers');
|
|
5
|
+
|
|
6
|
+
const printUsage = (_, actionArr) => {
|
|
7
|
+
const globalOptionDefinitions = [
|
|
8
|
+
{
|
|
9
|
+
name: 'verbose',
|
|
10
|
+
description: 'Be verbose',
|
|
11
|
+
alias: 'v',
|
|
12
|
+
group: 'global',
|
|
13
|
+
},
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const optionDefinitions = [
|
|
17
|
+
{
|
|
18
|
+
name: 'image',
|
|
19
|
+
description: 'Provide a custom image.',
|
|
20
|
+
alias: 'i',
|
|
21
|
+
typeLabel: '<docker-image>',
|
|
22
|
+
group: 'docker',
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const actions = {
|
|
27
|
+
help: {
|
|
28
|
+
name: 'help [action]',
|
|
29
|
+
summary: 'Display this help information or details for the given action.',
|
|
30
|
+
},
|
|
31
|
+
init: {
|
|
32
|
+
name: 'init',
|
|
33
|
+
summary: 'Create a libdragon project in the current directory.',
|
|
34
|
+
description: `Creates a libdragon project in the current directory. Every libdragon project will have its own docker container instance. If you are in a git repository or an NPM project, libdragon will be initialized at their root also marking there with a \`.libdragon\` folder.
|
|
35
|
+
|
|
36
|
+
A git repository and a submodule at \`./libdragon\` will also be created. Do not remove the \`.libdragon\` folder and commit its contents if you are using git, as it keeps persistent libdragon project information.
|
|
37
|
+
|
|
38
|
+
If this is the first time you are creating a libdragon project at that location, this action will also create skeleton project files to kickstart things with the given image, if provided. For subsequent runs, it will act like \`start\` thus can be used to revive an existing project without modifying it.`,
|
|
39
|
+
group: ['docker'],
|
|
40
|
+
},
|
|
41
|
+
make: {
|
|
42
|
+
name: 'make [params]',
|
|
43
|
+
summary: 'Run the libdragon build system in the current directory.',
|
|
44
|
+
description: `Runs the libdragon build system in the current directory. It will mirror your current working directory to the container.
|
|
45
|
+
|
|
46
|
+
This action is a shortcut to the \`exec\` action under the hood.`,
|
|
47
|
+
},
|
|
48
|
+
exec: {
|
|
49
|
+
name: 'exec <command>',
|
|
50
|
+
summary: 'Execute given command in the current directory.',
|
|
51
|
+
description: `Executes the given command in the container passing down any arguments provided. If you change your host working directory, the command will be executed in the corresponding folder in the container as well.
|
|
52
|
+
|
|
53
|
+
This action will first try to execute the command in the container and if the container is not accessible, it will attempt a complete \`start\` cycle.
|
|
54
|
+
|
|
55
|
+
This will properly passthrough your TTY if you have one. So by running \`libdragon exec bash\` you can start an interactive bash session with full TTY support.`,
|
|
56
|
+
},
|
|
57
|
+
start: {
|
|
58
|
+
name: 'start',
|
|
59
|
+
summary: 'Start the container for current project.',
|
|
60
|
+
description:
|
|
61
|
+
'Start the container assigned to the current libdragon project. Will first attempt to start an existing container if found, followed by a new container run and installation similar to the `install` action. Will always print out the container id on success.',
|
|
62
|
+
},
|
|
63
|
+
name: {
|
|
64
|
+
name: 'stop',
|
|
65
|
+
summary: 'Stop the container for current project.',
|
|
66
|
+
description:
|
|
67
|
+
'Stop the container assigned to the current libdragon project.',
|
|
68
|
+
},
|
|
69
|
+
install: {
|
|
70
|
+
name: 'install',
|
|
71
|
+
summary: 'Vendor libdragon as is.',
|
|
72
|
+
group: ['docker'],
|
|
73
|
+
description: `Attempts to build and install everything libdragon related into the container. This includes all the tools and third parties used by libdragon except for the toolchain. If you have made changes to libdragon, you can execute this action to build everything based on your changes. Requires you to have an intact \`libdragon\` at the root of the project. If you are not working on libdragon, you can just use the \`update\` action instead.
|
|
74
|
+
|
|
75
|
+
This can be useful to recover from a half-baked container.`,
|
|
76
|
+
},
|
|
77
|
+
update: {
|
|
78
|
+
name: 'update',
|
|
79
|
+
summary: 'Update libdragon and do an install.',
|
|
80
|
+
description:
|
|
81
|
+
'This action will update the submodule from the remote branch (`trunk`) with a merge strategy and then perform a `libdragon install`. You can use the `install` action to only update all libdragon related artifacts in the container.',
|
|
82
|
+
group: ['docker'],
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const actionsToShow = actionArr
|
|
87
|
+
?.filter((action) => Object.keys(actions).includes(action))
|
|
88
|
+
.filter((action) => !['help'].includes(action));
|
|
89
|
+
|
|
90
|
+
const sections = [
|
|
91
|
+
{
|
|
92
|
+
header: chalk.green('Usage:'),
|
|
93
|
+
content: 'libdragon [flags] <action>',
|
|
94
|
+
},
|
|
95
|
+
...(actionsToShow?.length
|
|
96
|
+
? actionsToShow.flatMap((action) => [
|
|
97
|
+
{
|
|
98
|
+
header: chalk.green(`${action} action:`),
|
|
99
|
+
content: actions[action].description,
|
|
100
|
+
},
|
|
101
|
+
actions[action].group
|
|
102
|
+
? {
|
|
103
|
+
header: `accepted flags:`,
|
|
104
|
+
optionList: optionDefinitions,
|
|
105
|
+
group: actions[action].group,
|
|
106
|
+
}
|
|
107
|
+
: {},
|
|
108
|
+
])
|
|
109
|
+
: [
|
|
110
|
+
{
|
|
111
|
+
header: chalk.green('Available Commands:'),
|
|
112
|
+
content: Object.values(actions).map((action) => ({
|
|
113
|
+
name: action.name,
|
|
114
|
+
summary: action.summary,
|
|
115
|
+
})),
|
|
116
|
+
},
|
|
117
|
+
]),
|
|
118
|
+
{
|
|
119
|
+
header: chalk.green('Global flags:'),
|
|
120
|
+
optionList: globalOptionDefinitions,
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
const usage = commandLineUsage(sections);
|
|
124
|
+
log(usage);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
module.exports = {
|
|
128
|
+
printUsage,
|
|
129
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libdragon",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.3.1",
|
|
4
4
|
"description": "This is a docker wrapper for libdragon",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"engines": {
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"url": "git+https://github.com/anacierdem/libdragon-docker.git"
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
|
-
"modules/**/*"
|
|
27
|
+
"modules/**/*",
|
|
28
|
+
"skeleton/**"
|
|
28
29
|
],
|
|
29
30
|
"author": "Ali Naci Erdem",
|
|
30
31
|
"license": "MIT",
|
|
@@ -34,10 +35,11 @@
|
|
|
34
35
|
"homepage": "https://github.com/anacierdem/libdragon-docker#readme",
|
|
35
36
|
"dependencies": {
|
|
36
37
|
"chalk": "^4.1.0",
|
|
38
|
+
"command-line-usage": "^6.1.1",
|
|
37
39
|
"lodash": "^4.17.20"
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
40
|
-
"ed64": "^2.0.
|
|
42
|
+
"ed64": "^2.0.3",
|
|
41
43
|
"eslint": "^7.32.0",
|
|
42
44
|
"pkg": "^5.3.1",
|
|
43
45
|
"prettier": "^2.4.0"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
V=1
|
|
2
|
+
SOURCE_DIR=src
|
|
3
|
+
BUILD_DIR=build
|
|
4
|
+
include $(N64_INST)/include/n64.mk
|
|
5
|
+
|
|
6
|
+
src=main.c
|
|
7
|
+
|
|
8
|
+
all: hello.z64
|
|
9
|
+
|
|
10
|
+
hello.z64: N64_ROM_TITLE="Hello World"
|
|
11
|
+
$(BUILD_DIR)/hello.elf: $(src:%.c=$(BUILD_DIR)/%.o)
|
|
12
|
+
|
|
13
|
+
clean:
|
|
14
|
+
rm -f $(BUILD_DIR)/* hello.z64
|
|
15
|
+
|
|
16
|
+
-include $(wildcard $(BUILD_DIR)/*.d)
|
|
17
|
+
|
|
18
|
+
.PHONY: all clean
|