libdragon 10.4.2 → 10.5.0
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 +28 -0
- package/index.js +19 -4
- package/modules/actions/clean.js +35 -0
- package/modules/actions/disasm.js +116 -0
- package/modules/actions/docker-utils.js +8 -0
- package/modules/actions/exec.js +15 -6
- package/modules/actions/help.js +19 -76
- package/modules/actions/index.js +5 -13
- package/modules/actions/init.js +30 -18
- package/modules/actions/install.js +8 -0
- package/modules/actions/make.js +19 -0
- package/modules/actions/npm-utils.js +122 -0
- package/modules/actions/start.js +12 -4
- package/modules/actions/stop.js +8 -0
- package/modules/actions/update.js +10 -4
- package/modules/actions/utils.js +6 -116
- package/modules/actions/version.js +17 -0
- package/modules/helpers.js +6 -1
- package/modules/project-info.js +19 -22
- package/package.json +1 -1
- package/skeleton/Makefile +8 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## [10.5.0] - 2022-04-09
|
|
4
|
+
### Fixed
|
|
5
|
+
|
|
6
|
+
- Fix a path bug that would cause incorrect behaviour when the command is run
|
|
7
|
+
deeper than a single level in the project folder.
|
|
8
|
+
- Fix a potential issue where `build.sh`might be incorrectly found inexistant
|
|
9
|
+
if the OS is picky about the paths to have native separators.
|
|
10
|
+
- Only save project information when necessary. Previously actions like `help`
|
|
11
|
+
were saving project info mistakenly.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `disasm` action to simplify disassembling ELF files generated by the toolchain.
|
|
16
|
+
- `version` action to display current version.
|
|
17
|
+
- `clean` action to remove the libdragon project.
|
|
18
|
+
- Additional documentation for flags.
|
|
19
|
+
- Print duration information when verbose.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Refactored out NPM related functions.
|
|
24
|
+
- Moved usage parameters to respective actions files as a refactor.
|
|
25
|
+
- It is possible to provide an absolute path to init `--directory` as long as it
|
|
26
|
+
is inside the project directory. Previously it was possible to provide somewhere
|
|
27
|
+
outside the project, but it would fail with an unexpected error.
|
|
28
|
+
- Simplify saving mechanism. Each action now internally resolves into the data
|
|
29
|
+
to save if any.
|
|
30
|
+
|
|
3
31
|
## [10.4.2] - 2022-04-03
|
|
4
32
|
|
|
5
33
|
### Fixed
|
package/index.js
CHANGED
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
CommandError,
|
|
16
16
|
ParameterError,
|
|
17
17
|
ValidationError,
|
|
18
|
+
log,
|
|
18
19
|
} = require('./modules/helpers');
|
|
19
20
|
const { readProjectInfo, writeProjectInfo } = require('./modules/project-info');
|
|
20
21
|
|
|
@@ -33,6 +34,7 @@ for (let i = 2; i < process.argv.length; i++) {
|
|
|
33
34
|
continue;
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
// TODO: we might move these to actions as well.
|
|
36
38
|
if (['--image', '-i'].includes(val)) {
|
|
37
39
|
options.DOCKER_IMAGE = process.argv[++i];
|
|
38
40
|
continue;
|
|
@@ -57,6 +59,14 @@ for (let i = 2; i < process.argv.length; i++) {
|
|
|
57
59
|
continue;
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
if (['--file', '-f'].includes(val)) {
|
|
63
|
+
options.FILE = process.argv[++i];
|
|
64
|
+
continue;
|
|
65
|
+
} else if (val.indexOf('--file=') === 0) {
|
|
66
|
+
options.FILE = val.split('=')[1];
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
60
70
|
if (val.indexOf('--') >= 0) {
|
|
61
71
|
console.error(chalk.red(`Invalid flag \`${val}\``));
|
|
62
72
|
printUsage();
|
|
@@ -115,6 +125,12 @@ if (
|
|
|
115
125
|
process.exit(STATUS_BAD_PARAM);
|
|
116
126
|
}
|
|
117
127
|
|
|
128
|
+
if (![actions.disasm].includes(currentAction) && options.FILE) {
|
|
129
|
+
console.error(chalk.red('Invalid flag: file'));
|
|
130
|
+
printUsage(undefined, [currentAction.name]);
|
|
131
|
+
process.exit(STATUS_BAD_PARAM);
|
|
132
|
+
}
|
|
133
|
+
|
|
118
134
|
readProjectInfo()
|
|
119
135
|
.then((info) =>
|
|
120
136
|
currentAction.fn(
|
|
@@ -160,10 +176,9 @@ readProjectInfo()
|
|
|
160
176
|
// We don't have a user targeted error anymore, we did a mistake for sure
|
|
161
177
|
process.exit(STATUS_ERROR);
|
|
162
178
|
})
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return writeProjectInfo();
|
|
166
|
-
})
|
|
179
|
+
// Everything was done, update the configuration file if not exiting early
|
|
180
|
+
.then(writeProjectInfo)
|
|
167
181
|
.finally(() => {
|
|
182
|
+
log(`Took: ${process.uptime()}s`, true);
|
|
168
183
|
process.exit(STATUS_OK);
|
|
169
184
|
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const fs = require('fs/promises');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const { mustHaveProject, destroyContainer } = require('./utils');
|
|
5
|
+
const { CONFIG_FILE, LIBDRAGON_PROJECT_MANIFEST } = require('../constants');
|
|
6
|
+
const { fileExists, dirExists } = require('../helpers');
|
|
7
|
+
|
|
8
|
+
const clean = async (libdragonInfo) => {
|
|
9
|
+
await mustHaveProject(libdragonInfo);
|
|
10
|
+
|
|
11
|
+
await destroyContainer(libdragonInfo);
|
|
12
|
+
|
|
13
|
+
const projectPath = path.join(libdragonInfo.root, LIBDRAGON_PROJECT_MANIFEST);
|
|
14
|
+
const configPath = path.join(projectPath, CONFIG_FILE);
|
|
15
|
+
|
|
16
|
+
if (await fileExists(configPath)) {
|
|
17
|
+
await fs.rm(configPath);
|
|
18
|
+
}
|
|
19
|
+
if (dirExists(projectPath)) {
|
|
20
|
+
await fs.rmdir(projectPath);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
name: 'clean',
|
|
26
|
+
fn: clean,
|
|
27
|
+
showStatus: true,
|
|
28
|
+
usage: {
|
|
29
|
+
name: 'clean',
|
|
30
|
+
summary: 'Do clean-up for current project.',
|
|
31
|
+
description: `Removes libdragon configuration from current project and removes any known containers but will not touch previously vendored files. \`libdragon\` will not work anymore for this project.
|
|
32
|
+
|
|
33
|
+
Must be run in an initialized libdragon project.`,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs/promises');
|
|
3
|
+
|
|
4
|
+
const { mustHaveProject } = require('./utils');
|
|
5
|
+
const { fn: exec } = require('./exec');
|
|
6
|
+
const {
|
|
7
|
+
ValidationError,
|
|
8
|
+
toPosixPath,
|
|
9
|
+
dirExists,
|
|
10
|
+
fileExists,
|
|
11
|
+
ParameterError,
|
|
12
|
+
} = require('../helpers');
|
|
13
|
+
|
|
14
|
+
const findElf = async (stop, start = '.') => {
|
|
15
|
+
start = path.resolve(start);
|
|
16
|
+
|
|
17
|
+
const files = await fs.readdir(start);
|
|
18
|
+
|
|
19
|
+
const buildDir = path.join(start, 'build');
|
|
20
|
+
if (await dirExists(buildDir)) {
|
|
21
|
+
const elfFile = await findElf(buildDir, buildDir);
|
|
22
|
+
if (elfFile) return elfFile;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const elfFiles = files.filter((name) => name.endsWith('.elf'));
|
|
26
|
+
|
|
27
|
+
if (elfFiles.length > 1) {
|
|
28
|
+
throw new ValidationError(
|
|
29
|
+
`Multiple ELF files found in ${path.resolve(
|
|
30
|
+
start
|
|
31
|
+
)}. Use --file to specify.`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (elfFiles.length === 1) {
|
|
36
|
+
return path.join(start, elfFiles[0]);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const parent = path.join(start, '..');
|
|
40
|
+
if (start !== stop) {
|
|
41
|
+
return await findElf(stop, parent);
|
|
42
|
+
} else {
|
|
43
|
+
throw new ValidationError(`No ELF files found. Use --file to specify.`);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const disasm = async (libdragonInfo, extraArgs) => {
|
|
48
|
+
await mustHaveProject(libdragonInfo);
|
|
49
|
+
|
|
50
|
+
let elfPath;
|
|
51
|
+
if (libdragonInfo.options.FILE) {
|
|
52
|
+
if (
|
|
53
|
+
path
|
|
54
|
+
.relative(libdragonInfo.root, libdragonInfo.options.FILE)
|
|
55
|
+
.startsWith('..')
|
|
56
|
+
) {
|
|
57
|
+
throw new ParameterError(
|
|
58
|
+
`Provided file ${libdragonInfo.options.FILE} is outside the project directory.`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
if (!(await fileExists(libdragonInfo.options.FILE)))
|
|
62
|
+
throw new ParameterError(
|
|
63
|
+
`Provided file ${libdragonInfo.options.FILE} does not exist`
|
|
64
|
+
);
|
|
65
|
+
elfPath = libdragonInfo.options.FILE;
|
|
66
|
+
}
|
|
67
|
+
elfPath = elfPath ?? (await findElf(libdragonInfo.root));
|
|
68
|
+
|
|
69
|
+
const haveSymbol = extraArgs.length > 0 && !extraArgs[0].startsWith('-');
|
|
70
|
+
|
|
71
|
+
const finalArgs = haveSymbol
|
|
72
|
+
? [`--disassemble=${extraArgs[0]}`, ...extraArgs.slice(1)]
|
|
73
|
+
: extraArgs;
|
|
74
|
+
|
|
75
|
+
const intermixSourceParams =
|
|
76
|
+
extraArgs.length === 0 || haveSymbol ? ['-S'] : [];
|
|
77
|
+
|
|
78
|
+
return await exec(libdragonInfo, [
|
|
79
|
+
'mips64-elf-objdump',
|
|
80
|
+
...finalArgs,
|
|
81
|
+
...intermixSourceParams,
|
|
82
|
+
toPosixPath(path.relative(process.cwd(), elfPath)),
|
|
83
|
+
]);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
module.exports = {
|
|
87
|
+
name: 'disasm',
|
|
88
|
+
fn: disasm,
|
|
89
|
+
showStatus: true,
|
|
90
|
+
forwardsRestParams: true,
|
|
91
|
+
usage: {
|
|
92
|
+
name: 'disasm [symbol|flags]',
|
|
93
|
+
summary: 'Disassemble the nearest *.elf file.',
|
|
94
|
+
description: `Executes \`objdump\` for the nearest *.elf file starting from the working directory, going up. If there is a \`build\` directory in the searched paths, checks inside it as well. Any extra flags are passed down to \`objdump\` before the filename.
|
|
95
|
+
|
|
96
|
+
If a symbol name is provided after the action, it is converted to \`--disassembly=<symbol>\` and intermixed source* (\`-S\`) is automatically applied. This allows disassembling a single symbol by running;
|
|
97
|
+
|
|
98
|
+
\`libdragon disasm main\`
|
|
99
|
+
|
|
100
|
+
Again, any following flags are forwarded down. If run without extra symbol/flags, disassembles whole ELF with \`-S\` by default.
|
|
101
|
+
|
|
102
|
+
Must be run in an initialized libdragon project.
|
|
103
|
+
|
|
104
|
+
* Note that to be able to see the source, the code must be built with \`D=1\``,
|
|
105
|
+
|
|
106
|
+
optionList: [
|
|
107
|
+
{
|
|
108
|
+
name: 'file',
|
|
109
|
+
description:
|
|
110
|
+
'Provide a specific ELF file relative to current working directory and inside the libdragon project.',
|
|
111
|
+
alias: 'f',
|
|
112
|
+
typeLabel: ' ',
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
};
|
package/modules/actions/exec.js
CHANGED
|
@@ -4,11 +4,8 @@ const { CONTAINER_TARGET_PATH } = require('../constants');
|
|
|
4
4
|
const { log, dockerExec, toPosixPath } = require('../helpers');
|
|
5
5
|
|
|
6
6
|
const { start } = require('./start');
|
|
7
|
-
const {
|
|
8
|
-
|
|
9
|
-
installDependencies,
|
|
10
|
-
mustHaveProject,
|
|
11
|
-
} = require('./utils');
|
|
7
|
+
const { dockerHostUserParams } = require('./docker-utils');
|
|
8
|
+
const { installDependencies, mustHaveProject } = require('./utils');
|
|
12
9
|
|
|
13
10
|
function dockerRelativeWorkdir(libdragonInfo) {
|
|
14
11
|
return (
|
|
@@ -72,7 +69,7 @@ const exec = async (libdragonInfo, commandAndParams) => {
|
|
|
72
69
|
if (!libdragonInfo.containerId) {
|
|
73
70
|
log(`Container does not exist for sure, restart`, true);
|
|
74
71
|
await startOnceAndCmd();
|
|
75
|
-
return;
|
|
72
|
+
return libdragonInfo;
|
|
76
73
|
}
|
|
77
74
|
|
|
78
75
|
try {
|
|
@@ -87,6 +84,7 @@ const exec = async (libdragonInfo, commandAndParams) => {
|
|
|
87
84
|
}
|
|
88
85
|
await startOnceAndCmd();
|
|
89
86
|
}
|
|
87
|
+
return libdragonInfo;
|
|
90
88
|
};
|
|
91
89
|
|
|
92
90
|
module.exports = {
|
|
@@ -94,4 +92,15 @@ module.exports = {
|
|
|
94
92
|
fn: exec,
|
|
95
93
|
forwardsRestParams: true,
|
|
96
94
|
showStatus: true,
|
|
95
|
+
usage: {
|
|
96
|
+
name: 'exec <command>',
|
|
97
|
+
summary: 'Execute given command in the current directory.',
|
|
98
|
+
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.
|
|
99
|
+
|
|
100
|
+
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.
|
|
101
|
+
|
|
102
|
+
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.
|
|
103
|
+
|
|
104
|
+
Must be run in an initialized libdragon project.`,
|
|
105
|
+
},
|
|
97
106
|
};
|
package/modules/actions/help.js
CHANGED
|
@@ -4,6 +4,7 @@ const commandLineUsage = require('command-line-usage');
|
|
|
4
4
|
const { log } = require('../helpers');
|
|
5
5
|
|
|
6
6
|
const printUsage = (_, actionArr) => {
|
|
7
|
+
const actions = require('./');
|
|
7
8
|
const globalOptionDefinitions = [
|
|
8
9
|
{
|
|
9
10
|
name: 'verbose',
|
|
@@ -11,7 +12,6 @@ const printUsage = (_, actionArr) => {
|
|
|
11
12
|
'Be verbose. This will print all commands dispatched and their outputs as well. Will also start printing error stack traces.',
|
|
12
13
|
alias: 'v',
|
|
13
14
|
typeLabel: ' ',
|
|
14
|
-
group: 'global',
|
|
15
15
|
},
|
|
16
16
|
];
|
|
17
17
|
|
|
@@ -26,7 +26,7 @@ const printUsage = (_, actionArr) => {
|
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
name: 'directory',
|
|
29
|
-
description: `Directory where libdragon files are expected. It must be
|
|
29
|
+
description: `Directory where libdragon files are expected. It must be inside the libdragon project as it will be mounted on the docker container. The cli will create and manage it when using a non-manual strategy. Defaults to \`./libdragon\` if not provided.\n`,
|
|
30
30
|
alias: 'd',
|
|
31
31
|
typeLabel: '<path>',
|
|
32
32
|
group: 'vendoring',
|
|
@@ -48,72 +48,6 @@ const printUsage = (_, actionArr) => {
|
|
|
48
48
|
},
|
|
49
49
|
];
|
|
50
50
|
|
|
51
|
-
// TODO: move these to respective actions
|
|
52
|
-
|
|
53
|
-
const actions = {
|
|
54
|
-
help: {
|
|
55
|
-
name: 'help [action]',
|
|
56
|
-
summary: 'Display this help information or details for the given action.',
|
|
57
|
-
},
|
|
58
|
-
init: {
|
|
59
|
-
name: 'init',
|
|
60
|
-
summary: 'Create a libdragon project in the current directory.',
|
|
61
|
-
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. Do not remove the \`.libdragon\` folder and commit its contents if you are using source control, as it keeps persistent libdragon project information.
|
|
62
|
-
|
|
63
|
-
By default, a git repository and a submodule at \`./libdragon\` will be created to automatically update the vendored libdragon files on subsequent \`update\`s. If you intend to opt-out from this feature, see the \`--strategy manual\` flag to provide your self-managed libdragon copy. The default behaviour is intended for users who primarily want to consume libdragon as is.
|
|
64
|
-
|
|
65
|
-
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.`,
|
|
66
|
-
group: ['docker', 'vendoring'],
|
|
67
|
-
},
|
|
68
|
-
make: {
|
|
69
|
-
name: 'make [params]',
|
|
70
|
-
summary: 'Run the libdragon build system in the current directory.',
|
|
71
|
-
description: `Runs the libdragon build system in the current directory. It will mirror your current working directory to the container.
|
|
72
|
-
|
|
73
|
-
Must be run in an initialized libdragon project. This action is a shortcut to the \`exec\` action under the hood.`,
|
|
74
|
-
},
|
|
75
|
-
exec: {
|
|
76
|
-
name: 'exec <command>',
|
|
77
|
-
summary: 'Execute given command in the current directory.',
|
|
78
|
-
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.
|
|
79
|
-
|
|
80
|
-
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.
|
|
81
|
-
|
|
82
|
-
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.
|
|
83
|
-
|
|
84
|
-
Must be run in an initialized libdragon project.`,
|
|
85
|
-
},
|
|
86
|
-
start: {
|
|
87
|
-
name: 'start',
|
|
88
|
-
summary: 'Start the container for current project.',
|
|
89
|
-
description: `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.
|
|
90
|
-
|
|
91
|
-
Must be run in an initialized libdragon project.`,
|
|
92
|
-
},
|
|
93
|
-
name: {
|
|
94
|
-
name: 'stop',
|
|
95
|
-
summary: 'Stop the container for current project.',
|
|
96
|
-
description: `Stop the container assigned to the current libdragon project.
|
|
97
|
-
|
|
98
|
-
Must be run in an initialized libdragon project.`,
|
|
99
|
-
},
|
|
100
|
-
install: {
|
|
101
|
-
name: 'install',
|
|
102
|
-
summary: 'Vendor libdragon as is.',
|
|
103
|
-
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 vendoring target (also see the \`--directory\` flag). If you are not working on libdragon itself, you can just use the \`update\` action instead.
|
|
104
|
-
|
|
105
|
-
Must be run in an initialized libdragon project. This can be useful to recover from a half-baked container.`,
|
|
106
|
-
},
|
|
107
|
-
update: {
|
|
108
|
-
name: 'update',
|
|
109
|
-
summary: 'Update libdragon and do an install.',
|
|
110
|
-
description: `Will update the docker image and if you are using auto-vendoring (see \`--strategy\`), will also update the submodule/subtree from the remote branch (\`trunk\`) with a merge/squash strategy and then perform a \`libdragon install\`. You can use the \`install\` action to only update all libdragon related artifacts in the container.
|
|
111
|
-
|
|
112
|
-
Must be run in an initialized libdragon project.`,
|
|
113
|
-
group: ['docker'],
|
|
114
|
-
},
|
|
115
|
-
};
|
|
116
|
-
|
|
117
51
|
const actionsToShow = actionArr
|
|
118
52
|
?.filter((action) => Object.keys(actions).includes(action))
|
|
119
53
|
.filter((action) => !['help'].includes(action));
|
|
@@ -121,28 +55,33 @@ const printUsage = (_, actionArr) => {
|
|
|
121
55
|
const sections = [
|
|
122
56
|
{
|
|
123
57
|
header: chalk.green('Usage:'),
|
|
124
|
-
content:
|
|
58
|
+
content: `libdragon [flags] <action>
|
|
59
|
+
|
|
60
|
+
For string flags valid syntax are: \`-i <value>\` or \`--image=<value>\``,
|
|
125
61
|
},
|
|
126
62
|
...(actionsToShow?.length
|
|
127
63
|
? actionsToShow.flatMap((action) => [
|
|
128
64
|
{
|
|
129
65
|
header: chalk.green(`${action} action:`),
|
|
130
|
-
content: actions[action].description,
|
|
66
|
+
content: actions[action].usage.description,
|
|
131
67
|
},
|
|
132
|
-
actions[action].group
|
|
68
|
+
actions[action].usage.group || actions[action].usage.optionList
|
|
133
69
|
? {
|
|
134
70
|
header: `accepted flags:`,
|
|
135
|
-
optionList:
|
|
136
|
-
|
|
71
|
+
optionList: [
|
|
72
|
+
...(actions[action].usage.group ? optionDefinitions : []),
|
|
73
|
+
...(actions[action].usage.optionList ?? []),
|
|
74
|
+
],
|
|
75
|
+
group: actions[action].usage.group,
|
|
137
76
|
}
|
|
138
77
|
: {},
|
|
139
78
|
])
|
|
140
79
|
: [
|
|
141
80
|
{
|
|
142
81
|
header: chalk.green('Available Commands:'),
|
|
143
|
-
content: Object.values(actions).map((
|
|
144
|
-
name:
|
|
145
|
-
summary:
|
|
82
|
+
content: Object.values(actions).map(({ usage }) => ({
|
|
83
|
+
name: usage.name,
|
|
84
|
+
summary: usage.summary,
|
|
146
85
|
})),
|
|
147
86
|
},
|
|
148
87
|
]),
|
|
@@ -160,4 +99,8 @@ module.exports = {
|
|
|
160
99
|
fn: printUsage,
|
|
161
100
|
showStatus: true,
|
|
162
101
|
forwardsRestParams: true,
|
|
102
|
+
usage: {
|
|
103
|
+
name: 'help [action]',
|
|
104
|
+
summary: 'Display this help information or details for the given action.',
|
|
105
|
+
},
|
|
163
106
|
};
|
package/modules/actions/index.js
CHANGED
|
@@ -1,25 +1,17 @@
|
|
|
1
|
-
const { fn: exec } = require('./exec');
|
|
2
|
-
|
|
3
|
-
const make = async (libdragonInfo, params) => {
|
|
4
|
-
await exec(libdragonInfo, ['make', ...params]);
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
// TODO: separate into files
|
|
8
1
|
module.exports = {
|
|
9
2
|
start: require('./start'),
|
|
10
3
|
stop: require('./stop'),
|
|
11
4
|
init: require('./init'),
|
|
12
5
|
|
|
13
6
|
exec: require('./exec'),
|
|
14
|
-
make:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
forwardsRestParams: true,
|
|
18
|
-
showStatus: true,
|
|
19
|
-
},
|
|
7
|
+
make: require('./make'),
|
|
8
|
+
|
|
9
|
+
disasm: require('./disasm'),
|
|
20
10
|
|
|
21
11
|
install: require('./install'),
|
|
22
12
|
update: require('./update'),
|
|
23
13
|
|
|
24
14
|
help: require('./help'),
|
|
15
|
+
version: require('./version'),
|
|
16
|
+
clean: require('./clean'),
|
|
25
17
|
};
|
package/modules/actions/init.js
CHANGED
|
@@ -24,8 +24,9 @@ const {
|
|
|
24
24
|
CommandError,
|
|
25
25
|
ParameterError,
|
|
26
26
|
ValidationError,
|
|
27
|
+
toPosixPath,
|
|
28
|
+
toNativePath,
|
|
27
29
|
} = require('../helpers');
|
|
28
|
-
const { setProjectInfoToSave } = require('../project-info');
|
|
29
30
|
|
|
30
31
|
const autoVendor = async (libdragonInfo) => {
|
|
31
32
|
await runGitMaybeHost(libdragonInfo, ['init']);
|
|
@@ -111,25 +112,24 @@ async function init(libdragonInfo) {
|
|
|
111
112
|
|
|
112
113
|
// Update the strategy information for the project if the flag is provided
|
|
113
114
|
if (newInfo.options.VENDOR_STRAT) {
|
|
114
|
-
newInfo =
|
|
115
|
-
...newInfo,
|
|
116
|
-
vendorStrategy: newInfo.options.VENDOR_STRAT,
|
|
117
|
-
});
|
|
115
|
+
newInfo.vendorStrategy = newInfo.options.VENDOR_STRAT;
|
|
118
116
|
}
|
|
119
117
|
|
|
120
118
|
// Update the directory information for the project if the flag is provided
|
|
121
119
|
if (newInfo.options.VENDOR_DIR) {
|
|
120
|
+
const relativeVendorDir = path.relative(
|
|
121
|
+
libdragonInfo.root,
|
|
122
|
+
libdragonInfo.options.VENDOR_DIR
|
|
123
|
+
);
|
|
122
124
|
// Validate vendoring path
|
|
123
|
-
if (
|
|
125
|
+
if (relativeVendorDir.startsWith('..')) {
|
|
124
126
|
throw new ParameterError(
|
|
125
|
-
|
|
127
|
+
`\`--directory=${libdragonInfo.options.VENDOR_DIR}\` is outside the project directory.`
|
|
126
128
|
);
|
|
127
129
|
}
|
|
128
130
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
vendorDirectory: newInfo.options.VENDOR_DIR,
|
|
132
|
-
});
|
|
131
|
+
// Immeditately convert it to a posix and relative path
|
|
132
|
+
newInfo.vendorDirectory = toPosixPath(relativeVendorDir);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
if (newInfo.haveProjectConfig) {
|
|
@@ -145,12 +145,11 @@ async function init(libdragonInfo) {
|
|
|
145
145
|
);
|
|
146
146
|
}
|
|
147
147
|
// TODO: we may make sure git and submodule is initialized here
|
|
148
|
-
await install(newInfo);
|
|
149
|
-
return;
|
|
148
|
+
return await install(newInfo);
|
|
150
149
|
}
|
|
151
150
|
|
|
152
|
-
newInfo.imageName
|
|
153
|
-
|
|
151
|
+
await updateImage(newInfo, newInfo.imageName);
|
|
152
|
+
|
|
154
153
|
// Download image and start it
|
|
155
154
|
const containerReadyPromise = start(newInfo, true).then((newId) => ({
|
|
156
155
|
...newInfo,
|
|
@@ -159,7 +158,10 @@ async function init(libdragonInfo) {
|
|
|
159
158
|
|
|
160
159
|
let vendorAndGitReadyPromise = containerReadyPromise;
|
|
161
160
|
if (newInfo.vendorStrategy !== 'manual') {
|
|
162
|
-
const vendorTarget = path.relative(
|
|
161
|
+
const vendorTarget = path.relative(
|
|
162
|
+
newInfo.root,
|
|
163
|
+
toNativePath(newInfo.vendorDirectory)
|
|
164
|
+
);
|
|
163
165
|
const [vendorTargetExists] = await Promise.all([
|
|
164
166
|
fs.stat(vendorTarget).catch((e) => {
|
|
165
167
|
if (e.code !== 'ENOENT') throw e;
|
|
@@ -171,8 +173,7 @@ async function init(libdragonInfo) {
|
|
|
171
173
|
if (vendorTargetExists) {
|
|
172
174
|
throw new ValidationError(
|
|
173
175
|
`${path.resolve(
|
|
174
|
-
|
|
175
|
-
newInfo.vendorDirectory
|
|
176
|
+
vendorTarget
|
|
176
177
|
)} already exists. That is the libdragon vendoring target, please remove and retry.`
|
|
177
178
|
);
|
|
178
179
|
}
|
|
@@ -192,10 +193,21 @@ async function init(libdragonInfo) {
|
|
|
192
193
|
]);
|
|
193
194
|
|
|
194
195
|
log(chalk.green(`libdragon ready at \`${newInfo.root}\`.`));
|
|
196
|
+
return newInfo;
|
|
195
197
|
}
|
|
196
198
|
|
|
197
199
|
module.exports = {
|
|
198
200
|
name: 'init',
|
|
199
201
|
fn: init,
|
|
200
202
|
showStatus: true,
|
|
203
|
+
usage: {
|
|
204
|
+
name: 'init',
|
|
205
|
+
summary: 'Create a libdragon project in the current directory.',
|
|
206
|
+
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. Do not remove the \`.libdragon\` folder and commit its contents if you are using source control, as it keeps persistent libdragon project information.
|
|
207
|
+
|
|
208
|
+
By default, a git repository and a submodule at \`./libdragon\` will be created to automatically update the vendored libdragon files on subsequent \`update\`s. If you intend to opt-out from this feature, see the \`--strategy manual\` flag to provide your self-managed libdragon copy. The default behaviour is intended for users who primarily want to consume libdragon as is.
|
|
209
|
+
|
|
210
|
+
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.`,
|
|
211
|
+
group: ['docker', 'vendoring'],
|
|
212
|
+
},
|
|
201
213
|
};
|
|
@@ -39,10 +39,18 @@ const install = async (libdragonInfo, skipUpdate) => {
|
|
|
39
39
|
// Re-install vendors on new image
|
|
40
40
|
// TODO: skip this if unnecessary
|
|
41
41
|
await installDependencies(updatedInfo);
|
|
42
|
+
return updatedInfo;
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
module.exports = {
|
|
45
46
|
name: 'install',
|
|
46
47
|
fn: install,
|
|
47
48
|
showStatus: true,
|
|
49
|
+
usage: {
|
|
50
|
+
name: 'install',
|
|
51
|
+
summary: 'Vendor libdragon as is.',
|
|
52
|
+
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 vendoring target (also see the \`--directory\` flag). If you are not working on libdragon itself, you can just use the \`update\` action instead.
|
|
53
|
+
|
|
54
|
+
Must be run in an initialized libdragon project. This can be useful to recover from a half-baked container.`,
|
|
55
|
+
},
|
|
48
56
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const { fn: exec } = require('./exec');
|
|
2
|
+
|
|
3
|
+
const make = async (libdragonInfo, params) => {
|
|
4
|
+
return await exec(libdragonInfo, ['make', ...params]);
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
name: 'make',
|
|
9
|
+
fn: make,
|
|
10
|
+
forwardsRestParams: true,
|
|
11
|
+
showStatus: true,
|
|
12
|
+
usage: {
|
|
13
|
+
name: 'make [params]',
|
|
14
|
+
summary: 'Run the libdragon build system in the current directory.',
|
|
15
|
+
description: `Runs the libdragon build system in the current directory. It will mirror your current working directory to the container.
|
|
16
|
+
|
|
17
|
+
Must be run in an initialized libdragon project. This action is a shortcut to the \`exec\` action under the hood.`,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fsClassic = require('fs');
|
|
3
|
+
|
|
4
|
+
const _ = require('lodash');
|
|
5
|
+
|
|
6
|
+
const { dockerHostUserParams } = require('./docker-utils');
|
|
7
|
+
|
|
8
|
+
const { CONTAINER_TARGET_PATH } = require('../constants');
|
|
9
|
+
const {
|
|
10
|
+
fileExists,
|
|
11
|
+
toPosixPath,
|
|
12
|
+
spawnProcess,
|
|
13
|
+
dockerExec,
|
|
14
|
+
ValidationError,
|
|
15
|
+
} = require('../helpers');
|
|
16
|
+
|
|
17
|
+
async function findNPMRoot() {
|
|
18
|
+
try {
|
|
19
|
+
const root = path.resolve((await runNPM(['root'])).trim(), '..');
|
|
20
|
+
// Only report if package.json really exists. npm fallbacks to cwd
|
|
21
|
+
if (await fileExists(path.join(root, 'package.json'))) {
|
|
22
|
+
return root;
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
// User does not have and does not care about NPM if it didn't work
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Install other NPM dependencies if this is an NPM project
|
|
31
|
+
const installNPMDependencies = async (libdragonInfo) => {
|
|
32
|
+
const npmRoot = await findNPMRoot();
|
|
33
|
+
if (npmRoot) {
|
|
34
|
+
const packageJsonPath = path.join(npmRoot, 'package.json');
|
|
35
|
+
|
|
36
|
+
const { dependencies, devDependencies } = require(packageJsonPath);
|
|
37
|
+
|
|
38
|
+
const deps = await Promise.all(
|
|
39
|
+
Object.keys({
|
|
40
|
+
...dependencies,
|
|
41
|
+
...devDependencies,
|
|
42
|
+
})
|
|
43
|
+
.filter((dep) => dep !== 'libdragon')
|
|
44
|
+
.map(async (dep) => {
|
|
45
|
+
const npmPath = await runNPM(['ls', dep, '--parseable=true']);
|
|
46
|
+
return {
|
|
47
|
+
name: dep,
|
|
48
|
+
paths: _.uniq(npmPath.split('\n').filter((f) => f)),
|
|
49
|
+
};
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
await Promise.all(
|
|
54
|
+
deps.map(({ name, paths }) => {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
fsClassic.access(
|
|
57
|
+
path.join(paths[0], 'Makefile'),
|
|
58
|
+
fsClassic.F_OK,
|
|
59
|
+
async (e) => {
|
|
60
|
+
if (e) {
|
|
61
|
+
// File does not exist - skip
|
|
62
|
+
resolve();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (paths.length > 1) {
|
|
67
|
+
reject(
|
|
68
|
+
new ValidationError(
|
|
69
|
+
`Using same dependency with different versions is not supported! ${name}`
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const relativePath = toPosixPath(
|
|
77
|
+
path.relative(libdragonInfo.root, paths[0])
|
|
78
|
+
);
|
|
79
|
+
const containerPath = path.posix.join(
|
|
80
|
+
CONTAINER_TARGET_PATH,
|
|
81
|
+
relativePath
|
|
82
|
+
);
|
|
83
|
+
const makePath = path.posix.join(containerPath, 'Makefile');
|
|
84
|
+
|
|
85
|
+
await dockerExec(
|
|
86
|
+
libdragonInfo,
|
|
87
|
+
[...dockerHostUserParams(libdragonInfo)],
|
|
88
|
+
[
|
|
89
|
+
'/bin/bash',
|
|
90
|
+
'-c',
|
|
91
|
+
'[ -f ' +
|
|
92
|
+
makePath +
|
|
93
|
+
' ] && make -C ' +
|
|
94
|
+
containerPath +
|
|
95
|
+
' && make -C ' +
|
|
96
|
+
containerPath +
|
|
97
|
+
' install',
|
|
98
|
+
]
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
resolve();
|
|
102
|
+
} catch (e) {
|
|
103
|
+
reject(e);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
function runNPM(params) {
|
|
114
|
+
return spawnProcess(
|
|
115
|
+
/^win/.test(process.platform) ? 'npm.cmd' : 'npm',
|
|
116
|
+
params
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
module.exports = {
|
|
120
|
+
installNPMDependencies,
|
|
121
|
+
findNPMRoot,
|
|
122
|
+
};
|
package/modules/actions/start.js
CHANGED
|
@@ -2,7 +2,6 @@ const chalk = require('chalk');
|
|
|
2
2
|
|
|
3
3
|
const { CONTAINER_TARGET_PATH } = require('../constants');
|
|
4
4
|
const { spawnProcess, log, dockerExec } = require('../helpers');
|
|
5
|
-
const { setProjectInfoToSave } = require('../project-info');
|
|
6
5
|
|
|
7
6
|
const {
|
|
8
7
|
checkContainerAndClean,
|
|
@@ -76,8 +75,6 @@ const initContainer = async (libdragonInfo) => {
|
|
|
76
75
|
chalk.green(`Successfully initialized docker container: ${name.trim()}`)
|
|
77
76
|
);
|
|
78
77
|
|
|
79
|
-
// Schedule an update to write image name
|
|
80
|
-
setProjectInfoToSave(libdragonInfo);
|
|
81
78
|
return newId;
|
|
82
79
|
};
|
|
83
80
|
|
|
@@ -108,7 +105,18 @@ const start = async (libdragonInfo, skipProjectCheck) => {
|
|
|
108
105
|
|
|
109
106
|
module.exports = {
|
|
110
107
|
name: 'start',
|
|
111
|
-
fn: async (libdragonInfo) =>
|
|
108
|
+
fn: async (libdragonInfo) => {
|
|
109
|
+
const containerId = await start(libdragonInfo);
|
|
110
|
+
log(containerId);
|
|
111
|
+
return { ...libdragonInfo, containerId };
|
|
112
|
+
},
|
|
112
113
|
start,
|
|
113
114
|
showStatus: false, // This will only print out the id
|
|
115
|
+
usage: {
|
|
116
|
+
name: 'start',
|
|
117
|
+
summary: 'Start the container for current project.',
|
|
118
|
+
description: `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.
|
|
119
|
+
|
|
120
|
+
Must be run in an initialized libdragon project.`,
|
|
121
|
+
},
|
|
114
122
|
};
|
package/modules/actions/stop.js
CHANGED
|
@@ -12,10 +12,18 @@ const stop = async (libdragonInfo) => {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
await spawnProcess('docker', ['container', 'stop', running]);
|
|
15
|
+
return libdragonInfo;
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
module.exports = {
|
|
18
19
|
name: 'stop',
|
|
19
20
|
fn: stop,
|
|
20
21
|
showStatus: false, // This will only print out the id
|
|
22
|
+
usage: {
|
|
23
|
+
name: 'stop',
|
|
24
|
+
summary: 'Stop the container for current project.',
|
|
25
|
+
description: `Stop the container assigned to the current libdragon project.
|
|
26
|
+
|
|
27
|
+
Must be run in an initialized libdragon project.`,
|
|
28
|
+
},
|
|
21
29
|
};
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
|
|
3
1
|
const { log } = require('../helpers');
|
|
4
2
|
const { LIBDRAGON_GIT, LIBDRAGON_BRANCH } = require('../constants');
|
|
5
3
|
const { runGitMaybeHost, mustHaveProject } = require('./utils');
|
|
@@ -27,7 +25,7 @@ const update = async (libdragonInfo) => {
|
|
|
27
25
|
'subtree',
|
|
28
26
|
'pull',
|
|
29
27
|
'--prefix',
|
|
30
|
-
|
|
28
|
+
libdragonInfo.vendorDirectory,
|
|
31
29
|
LIBDRAGON_GIT,
|
|
32
30
|
LIBDRAGON_BRANCH,
|
|
33
31
|
'--squash',
|
|
@@ -36,11 +34,19 @@ const update = async (libdragonInfo) => {
|
|
|
36
34
|
|
|
37
35
|
// The second parameter forces it to skip the image update step as we already
|
|
38
36
|
// do that above.
|
|
39
|
-
await install(newInfo, true);
|
|
37
|
+
return await install(newInfo, true);
|
|
40
38
|
};
|
|
41
39
|
|
|
42
40
|
module.exports = {
|
|
43
41
|
name: 'update',
|
|
44
42
|
fn: update,
|
|
45
43
|
showStatus: true,
|
|
44
|
+
usage: {
|
|
45
|
+
name: 'update',
|
|
46
|
+
summary: 'Update libdragon and do an install.',
|
|
47
|
+
description: `Will update the docker image and if you are using auto-vendoring (see \`--strategy\`), will also update the submodule/subtree from the remote branch (\`trunk\`) with a merge/squash strategy and then perform a \`libdragon install\`. You can use the \`install\` action to only update all libdragon related artifacts in the container.
|
|
48
|
+
|
|
49
|
+
Must be run in an initialized libdragon project.`,
|
|
50
|
+
group: ['docker'],
|
|
51
|
+
},
|
|
46
52
|
};
|
package/modules/actions/utils.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
-
const fsClassic = require('fs');
|
|
3
2
|
const fs = require('fs/promises');
|
|
4
3
|
|
|
5
|
-
const _ = require('lodash');
|
|
6
|
-
|
|
7
4
|
const {
|
|
8
5
|
CONTAINER_TARGET_PATH,
|
|
9
6
|
CACHED_CONTAINER_FILE,
|
|
@@ -12,7 +9,6 @@ const {
|
|
|
12
9
|
const {
|
|
13
10
|
fileExists,
|
|
14
11
|
log,
|
|
15
|
-
toPosixPath,
|
|
16
12
|
spawnProcess,
|
|
17
13
|
dockerExec,
|
|
18
14
|
dirExists,
|
|
@@ -20,30 +16,16 @@ const {
|
|
|
20
16
|
CommandError,
|
|
21
17
|
ValidationError,
|
|
22
18
|
ParameterError,
|
|
19
|
+
toNativePath,
|
|
23
20
|
} = require('../helpers');
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return ['-u', `${uid >= 0 ? uid : ''}:${gid >= 0 ? gid : ''}`];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async function findNPMRoot() {
|
|
31
|
-
try {
|
|
32
|
-
const root = path.resolve((await runNPM(['root'])).trim(), '..');
|
|
33
|
-
// Only report if package.json really exists. npm fallbacks to cwd
|
|
34
|
-
if (await fileExists(path.join(root, 'package.json'))) {
|
|
35
|
-
return root;
|
|
36
|
-
}
|
|
37
|
-
} catch {
|
|
38
|
-
// User does not have and does not care about NPM if it didn't work
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
22
|
+
const { dockerHostUserParams } = require('./docker-utils');
|
|
23
|
+
const { installNPMDependencies } = require('./npm-utils');
|
|
42
24
|
|
|
43
25
|
const installDependencies = async (libdragonInfo) => {
|
|
44
26
|
const buildScriptPath = path.join(
|
|
45
27
|
libdragonInfo.root,
|
|
46
|
-
libdragonInfo.vendorDirectory,
|
|
28
|
+
toNativePath(libdragonInfo.vendorDirectory),
|
|
47
29
|
'build.sh'
|
|
48
30
|
);
|
|
49
31
|
if (!(await fileExists(buildScriptPath))) {
|
|
@@ -58,96 +40,13 @@ const installDependencies = async (libdragonInfo) => {
|
|
|
58
40
|
libdragonInfo,
|
|
59
41
|
[
|
|
60
42
|
'--workdir',
|
|
61
|
-
CONTAINER_TARGET_PATH +
|
|
62
|
-
'/' +
|
|
63
|
-
toPosixPath(
|
|
64
|
-
path.relative(libdragonInfo.root, libdragonInfo.vendorDirectory)
|
|
65
|
-
),
|
|
43
|
+
CONTAINER_TARGET_PATH + '/' + libdragonInfo.vendorDirectory,
|
|
66
44
|
...dockerHostUserParams(libdragonInfo),
|
|
67
45
|
],
|
|
68
46
|
['/bin/bash', './build.sh']
|
|
69
47
|
);
|
|
70
48
|
|
|
71
|
-
|
|
72
|
-
const npmRoot = await findNPMRoot();
|
|
73
|
-
if (npmRoot) {
|
|
74
|
-
const packageJsonPath = path.join(npmRoot, 'package.json');
|
|
75
|
-
|
|
76
|
-
const { dependencies, devDependencies } = require(packageJsonPath);
|
|
77
|
-
|
|
78
|
-
const deps = await Promise.all(
|
|
79
|
-
Object.keys({
|
|
80
|
-
...dependencies,
|
|
81
|
-
...devDependencies,
|
|
82
|
-
})
|
|
83
|
-
.filter((dep) => dep !== 'libdragon')
|
|
84
|
-
.map(async (dep) => {
|
|
85
|
-
const npmPath = await runNPM(['ls', dep, '--parseable=true']);
|
|
86
|
-
return {
|
|
87
|
-
name: dep,
|
|
88
|
-
paths: _.uniq(npmPath.split('\n').filter((f) => f)),
|
|
89
|
-
};
|
|
90
|
-
})
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
await Promise.all(
|
|
94
|
-
deps.map(({ name, paths }) => {
|
|
95
|
-
return new Promise((resolve, reject) => {
|
|
96
|
-
fsClassic.access(
|
|
97
|
-
path.join(paths[0], 'Makefile'),
|
|
98
|
-
fsClassic.F_OK,
|
|
99
|
-
async (e) => {
|
|
100
|
-
if (e) {
|
|
101
|
-
// File does not exist - skip
|
|
102
|
-
resolve();
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (paths.length > 1) {
|
|
107
|
-
reject(
|
|
108
|
-
new ValidationError(
|
|
109
|
-
`Using same dependency with different versions is not supported! ${name}`
|
|
110
|
-
)
|
|
111
|
-
);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
try {
|
|
116
|
-
const relativePath = toPosixPath(
|
|
117
|
-
path.relative(libdragonInfo.root, paths[0])
|
|
118
|
-
);
|
|
119
|
-
const containerPath = path.posix.join(
|
|
120
|
-
CONTAINER_TARGET_PATH,
|
|
121
|
-
relativePath
|
|
122
|
-
);
|
|
123
|
-
const makePath = path.posix.join(containerPath, 'Makefile');
|
|
124
|
-
|
|
125
|
-
await dockerExec(
|
|
126
|
-
libdragonInfo,
|
|
127
|
-
[...dockerHostUserParams(libdragonInfo)],
|
|
128
|
-
[
|
|
129
|
-
'/bin/bash',
|
|
130
|
-
'-c',
|
|
131
|
-
'[ -f ' +
|
|
132
|
-
makePath +
|
|
133
|
-
' ] && make -C ' +
|
|
134
|
-
containerPath +
|
|
135
|
-
' && make -C ' +
|
|
136
|
-
containerPath +
|
|
137
|
-
' install',
|
|
138
|
-
]
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
resolve();
|
|
142
|
-
} catch (e) {
|
|
143
|
-
reject(e);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
);
|
|
147
|
-
});
|
|
148
|
-
})
|
|
149
|
-
);
|
|
150
|
-
}
|
|
49
|
+
await installNPMDependencies(libdragonInfo);
|
|
151
50
|
};
|
|
152
51
|
|
|
153
52
|
/**
|
|
@@ -249,13 +148,6 @@ async function runGitMaybeHost(libdragonInfo, params, interactive = 'full') {
|
|
|
249
148
|
}
|
|
250
149
|
}
|
|
251
150
|
|
|
252
|
-
function runNPM(params) {
|
|
253
|
-
return spawnProcess(
|
|
254
|
-
/^win/.test(process.platform) ? 'npm.cmd' : 'npm',
|
|
255
|
-
params
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
151
|
async function checkContainerAndClean(libdragonInfo) {
|
|
260
152
|
const id =
|
|
261
153
|
libdragonInfo.containerId &&
|
|
@@ -319,9 +211,7 @@ module.exports = {
|
|
|
319
211
|
destroyContainer,
|
|
320
212
|
checkContainerRunning,
|
|
321
213
|
checkContainerAndClean,
|
|
322
|
-
dockerHostUserParams,
|
|
323
214
|
tryCacheContainerId,
|
|
324
215
|
runGitMaybeHost,
|
|
325
|
-
findNPMRoot,
|
|
326
216
|
mustHaveProject,
|
|
327
217
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const { version } = require('../../package.json');
|
|
2
|
+
const { log } = require('../helpers');
|
|
3
|
+
|
|
4
|
+
const printVersion = async () => {
|
|
5
|
+
log(`libdragon-cli v${version}`);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
name: 'version',
|
|
10
|
+
fn: printVersion,
|
|
11
|
+
showStatus: false,
|
|
12
|
+
usage: {
|
|
13
|
+
name: 'version',
|
|
14
|
+
summary: 'Display cli version.',
|
|
15
|
+
description: `Displays currently running cli version.`,
|
|
16
|
+
},
|
|
17
|
+
};
|
package/modules/helpers.js
CHANGED
|
@@ -209,7 +209,11 @@ async function copyDirContents(src, dst) {
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
function toPosixPath(p) {
|
|
212
|
-
return p.replace(new RegExp('\\' + path.sep), path.posix.sep);
|
|
212
|
+
return p.replace(new RegExp('\\' + path.sep, 'g'), path.posix.sep);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function toNativePath(p) {
|
|
216
|
+
return p.replace(new RegExp('\\' + path.posix.sep, 'g'), path.sep);
|
|
213
217
|
}
|
|
214
218
|
|
|
215
219
|
function assert(condition, error) {
|
|
@@ -236,6 +240,7 @@ function log(text, verboseOnly = false) {
|
|
|
236
240
|
module.exports = {
|
|
237
241
|
spawnProcess,
|
|
238
242
|
toPosixPath,
|
|
243
|
+
toNativePath,
|
|
239
244
|
log,
|
|
240
245
|
dockerExec,
|
|
241
246
|
assert,
|
package/modules/project-info.js
CHANGED
|
@@ -4,11 +4,12 @@ const fs = require('fs/promises');
|
|
|
4
4
|
|
|
5
5
|
const {
|
|
6
6
|
checkContainerAndClean,
|
|
7
|
-
findNPMRoot,
|
|
8
7
|
runGitMaybeHost,
|
|
9
8
|
tryCacheContainerId,
|
|
10
9
|
} = require('./actions/utils');
|
|
11
10
|
|
|
11
|
+
const { findNPMRoot } = require('./actions/npm-utils');
|
|
12
|
+
|
|
12
13
|
const {
|
|
13
14
|
LIBDRAGON_PROJECT_MANIFEST,
|
|
14
15
|
CONFIG_FILE,
|
|
@@ -20,7 +21,13 @@ const {
|
|
|
20
21
|
CONTAINER_TARGET_PATH,
|
|
21
22
|
} = require('./constants');
|
|
22
23
|
|
|
23
|
-
const {
|
|
24
|
+
const {
|
|
25
|
+
fileExists,
|
|
26
|
+
log,
|
|
27
|
+
spawnProcess,
|
|
28
|
+
toPosixPath,
|
|
29
|
+
assert,
|
|
30
|
+
} = require('./helpers');
|
|
24
31
|
|
|
25
32
|
async function findContainerId(libdragonInfo) {
|
|
26
33
|
const idFile = path.join(libdragonInfo.root, '.git', CACHED_CONTAINER_FILE);
|
|
@@ -96,7 +103,7 @@ async function readProjectInfo() {
|
|
|
96
103
|
// Set the defaults immediately, these should be present at all times even
|
|
97
104
|
// if we are migrating from the old config because they did not exist before
|
|
98
105
|
imageName: DOCKER_HUB_IMAGE,
|
|
99
|
-
vendorDirectory: path.join('.', LIBDRAGON_SUBMODULE),
|
|
106
|
+
vendorDirectory: toPosixPath(path.join('.', LIBDRAGON_SUBMODULE)),
|
|
100
107
|
vendorStrategy: DEFAULT_STRATEGY,
|
|
101
108
|
};
|
|
102
109
|
|
|
@@ -158,30 +165,16 @@ async function readProjectInfo() {
|
|
|
158
165
|
log(`Active vendor directory: ${info.vendorDirectory}`, true);
|
|
159
166
|
log(`Active vendor strategy: ${info.vendorStrategy}`, true);
|
|
160
167
|
|
|
161
|
-
// Cache the latest image name
|
|
162
|
-
setProjectInfoToSave(info);
|
|
163
168
|
return info;
|
|
164
169
|
}
|
|
165
170
|
|
|
166
|
-
let projectInfoToWrite = {};
|
|
167
171
|
/**
|
|
168
|
-
* Updates project info to be written. The provided keys are overwritten without
|
|
169
|
-
* changing the existing values. When the process exists successfully these will
|
|
170
|
-
* get written to the configuration file. Echoes back the given info.
|
|
171
172
|
* @param info This is only the base info without action properties like showStatus
|
|
172
173
|
* fn and command line options
|
|
173
174
|
*/
|
|
174
|
-
function
|
|
175
|
-
projectInfoToWrite = { ...projectInfoToWrite, ...info };
|
|
176
|
-
return info;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* @param info This is only the base info without action properties like showStatus
|
|
181
|
-
* fn and command line options
|
|
182
|
-
*/
|
|
183
|
-
async function writeProjectInfo(info = projectInfoToWrite) {
|
|
175
|
+
async function writeProjectInfo(info) {
|
|
184
176
|
// Do not log anything here as it may litter the output being always run on exit
|
|
177
|
+
if (!info) return;
|
|
185
178
|
|
|
186
179
|
const projectPath = path.join(info.root, LIBDRAGON_PROJECT_MANIFEST);
|
|
187
180
|
|
|
@@ -195,13 +188,17 @@ async function writeProjectInfo(info = projectInfoToWrite) {
|
|
|
195
188
|
await fs.mkdir(projectPath);
|
|
196
189
|
}
|
|
197
190
|
|
|
191
|
+
assert(
|
|
192
|
+
toPosixPath(info.vendorDirectory) === info.vendorDirectory,
|
|
193
|
+
new Error('vendorDirectory should always be in posix format')
|
|
194
|
+
);
|
|
195
|
+
|
|
198
196
|
await fs.writeFile(
|
|
199
197
|
path.join(projectPath, CONFIG_FILE),
|
|
200
198
|
JSON.stringify(
|
|
201
199
|
{
|
|
202
200
|
imageName: info.imageName,
|
|
203
|
-
|
|
204
|
-
vendorDirectory: toPosixPath(info.vendorDirectory),
|
|
201
|
+
vendorDirectory: info.vendorDirectory,
|
|
205
202
|
vendorStrategy: info.vendorStrategy,
|
|
206
203
|
},
|
|
207
204
|
null,
|
|
@@ -211,4 +208,4 @@ async function writeProjectInfo(info = projectInfoToWrite) {
|
|
|
211
208
|
log(`Configuration file updated`, true);
|
|
212
209
|
}
|
|
213
210
|
|
|
214
|
-
module.exports = { readProjectInfo, writeProjectInfo
|
|
211
|
+
module.exports = { readProjectInfo, writeProjectInfo };
|
package/package.json
CHANGED
package/skeleton/Makefile
CHANGED
|
@@ -3,16 +3,17 @@ SOURCE_DIR=src
|
|
|
3
3
|
BUILD_DIR=build
|
|
4
4
|
include $(N64_INST)/include/n64.mk
|
|
5
5
|
|
|
6
|
-
src=main.c
|
|
7
|
-
|
|
8
6
|
all: hello.z64
|
|
7
|
+
.PHONY: all
|
|
8
|
+
|
|
9
|
+
OBJS = $(BUILD_DIR)/main.o
|
|
9
10
|
|
|
10
11
|
hello.z64: N64_ROM_TITLE="Hello World"
|
|
11
|
-
$(BUILD_DIR)/hello.elf: $(src:%.c=$(BUILD_DIR)/%.o)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
rm -f $(BUILD_DIR)/* hello.z64
|
|
13
|
+
$(BUILD_DIR)/hello.elf: $(OBJS)
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
clean:
|
|
16
|
+
rm -f $(BUILD_DIR) *.z64
|
|
17
|
+
.PHONY: clean
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
-include $(wildcard $(BUILD_DIR)/*.d)
|