libdragon 10.9.1 → 11.0.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/README.md CHANGED
@@ -53,7 +53,9 @@ Download the [pre-built executable](https://github.com/anacierdem/libdragon-dock
53
53
 
54
54
  Install [node.js](https://nodejs.org/en/download/) (`>= 18`) and install `libdragon` as a global NPM package;
55
55
 
56
- npm install -g libdragon
56
+ ```bash
57
+ npm install -g libdragon
58
+ ```
57
59
 
58
60
  To update the tool to the latest, do `npm i -g libdragon@latest`.
59
61
 
@@ -61,19 +63,25 @@ To update the tool to the latest, do `npm i -g libdragon@latest`.
61
63
 
62
64
  Navigate to the folder you want to initialize your project and invoke libdragon;
63
65
 
64
- libdragon init
66
+ ```bash
67
+ libdragon init
68
+ ```
65
69
 
66
- On first `init` an example project will be created, the container will be downloaded, started and latest libdragon will get installed on it with all the example ROMs built. You can find all the example ROMs in the `libdragon` folder.
70
+ On first `init` an example project will be created, the container will be downloaded, started and latest libdragon will get installed on it with all the example ROMs built. You can find all the example ROMs in the `libdragon/examples` folder.
67
71
 
68
72
  The container's `make` can be invoked on your current working directory via;
69
73
 
70
- libdragon make
74
+ ```bash
75
+ libdragon make
76
+ ```
71
77
 
72
78
  Any additonal parameters are passed down to the actual make command. You can work on the files simultaneously with the docker container and any built artifacts will be available in project directory as it is mounted on the container.
73
79
 
74
80
  To update the library and rebuild/install all the artifacts;
75
81
 
76
- libdragon update
82
+ ```bash
83
+ libdragon update
84
+ ```
77
85
 
78
86
  In general, you can invoke libdragon as follows;
79
87
 
@@ -87,33 +95,40 @@ Run `libdragon help [action]` for more details on individual actions.
87
95
 
88
96
  Initialize your project as usual:
89
97
 
90
- libdragon init
98
+ ```bash
99
+ libdragon init
100
+ ```
91
101
 
92
102
  Then switch the submodule to the desired branch:
93
103
 
94
- cd ./libdragon
95
- git checkout opengl
96
- cd ..
97
- libdragon install
104
+ ```bash
105
+ git -C ./libdragon checkout opengl
106
+ libdragon install
107
+ ```
98
108
 
99
- If your changes are on a different remote, then you will need to manage your git remotes as usual.
109
+ If your changes are on a different remote, then you will need to manage your git remotes as usual. If you also want to update the remote tracking branch for the submodule, run:
110
+
111
+ ```bash
112
+ git submodule set-branch -b opengl libdragon
113
+ ```
114
+
115
+ This will update the branch on `.gitmodules` and if you commit that change, subsequent initializations will use the `opengl` branch by default.
100
116
 
101
117
  ### Testing changes on libdragon
102
118
 
103
119
  As libdragon is an actively developed library, you may find yourself at a position where you want to change a few things on it and see how it works. In general, if you modify the files in `libdragon` folder of your project, you can install that version to the docker container by simply running:
104
120
 
105
- libdragon install
121
+ ```bash
122
+ libdragon install
123
+ ```
106
124
 
107
125
  This will update all the artifacts in your container and your new code will start linking against the new version when you re-build it via `libdragon make`. The build system should pick up the change in the library and re-compile the dependent files.
108
126
 
109
- Instead of depending on the above command, you can re-build the library by making it a make dependency in your project:
127
+ Instead of depending on the above command, you can automatically re-build the library by making it a make dependency in your project:
110
128
 
111
129
  ```makefile
112
- libdragon-install: libdragon
130
+ libdragon-install:
113
131
  $(MAKE) -C ./libdragon install
114
-
115
- libdragon:
116
- $(MAKE) -C ./libdragon
117
132
  ```
118
133
 
119
134
  If your build now depends on `libdragon-install`, it will force an install (which should be pretty quick if you don't have changes) and force the build system to rebuild your project when necessary.
@@ -134,15 +149,21 @@ To be able to share your project with the library change, you just commit your c
134
149
 
135
150
  After cloning this repository on a system with node.js (`>= 18`) & docker (`>= 24`), in this repository's root do;
136
151
 
137
- npm install
152
+ ```bash
153
+ npm install
154
+ ```
138
155
 
139
156
  This will install all necessary NPM dependencies. Now it is time to get the original libdragon repository. (you can also clone this repository with `--recurse-submodules`)
140
157
 
141
- git submodule update --init
158
+ ```bash
159
+ git submodule update --init
160
+ ```
142
161
 
143
162
  Then run;
144
163
 
145
- npm run libdragon -- init
164
+ ```bash
165
+ npm run libdragon -- init
166
+ ```
146
167
 
147
168
  to download the pre-built toolchain image, start and initialize it. This will also install [test bench](#local-test-bench) dependencies into the container if any.
148
169
 
@@ -151,53 +172,76 @@ Now you will be able to work on the files simultaneously with the docker contain
151
172
  There is a root `Makefile` making deeper makefiles easier with these recipes;
152
173
 
153
174
  bench: build the test bench (see below)
154
- examples: re-build libdragon examples
155
- tests: re-build the test ROM
156
- libdragon: build libdragon itself
157
- libdragon-install: install libdragon
175
+ examples: build libdragon examples
176
+ tests: build the test ROM
177
+ libdragon-install: build and install libdragon
158
178
  clean-bench: clean the test bench (see below)
159
179
  clean: clean everything and start from scratch
160
180
 
161
181
  For example, to re-build the original libdragon examples do;
162
182
 
163
- npm run libdragon -- make examples
183
+ ```bash
184
+ npm run libdragon -- make examples
185
+ ```
164
186
 
165
187
  Similarly to run the `clean` recipe, run;
166
188
 
167
- npm run libdragon -- make clean
189
+ ```bash
190
+ npm run libdragon -- make clean
191
+ ```
168
192
 
169
193
  Keep in mind that `--` is necessary for actual arguments when using npm scripts.
170
194
 
171
195
  To update the submodule and re-build everything;
172
196
 
173
- npm run libdragon -- update
197
+ ```bash
198
+ npm run libdragon -- update
199
+ ```
174
200
 
175
201
  ### Local test bench
176
202
 
177
- This repository also uses [ed64](https://github.com/anacierdem/ed64), so you can just hit F5 on vscode (The `Run Test Bench` launch configuration) to run the test code in `src` folder to develop libdragon itself quicker if you have an everdrive.
203
+ The root `bench` recipe is for building the code in root `src` folder. This is a quick way of testing your libdragon changes or a sample code built around libdragon, and is called the test bench. This recipe together with `examples` and `tests` recipes will build and install libdragon automatically as necessary. Thus, they will always produce a rom image using the libdragon code in the repository via their make dependencies, which is ideal for experimenting with libdragon itself.
178
204
 
179
- There are also additional vscode launch configurations to build libdragon examples and tests based on the currently built and installed libdragon in the docker container. Most of these will always rebuild so that they will use the latest if you made and installed an alternative libdragon. The test bench itself and a few examples (that use the new build system) will already rebuild and reinstall libdragon automatically. These will always produce a rom image using the latest libdragon code in the active repository via its make dependencies. You can clean everything with the `clean` task (open the command palette and choose `Run Task -> clean`).
205
+ There are also vscode launch configurations to quickly build the examples, tests and the bench. If you have the `N64_EMU` environment variable set pointing at your favourite emulator ([ares](https://ares-emu.net/) is highly recommended), the launch configurations ending in `(emu)` will kick your emulator with the selected example/code. You can also just hit F5 to launch the selected configuration.
206
+
207
+ This repository also uses [ed64](https://github.com/anacierdem/ed64), so you can use the launch configurations without `(emu)` to run the code if you have an everdrive plugged in and your active configuration will just boot up.
208
+
209
+ You can clean everything with the `clean` recipe/task (open the command palette and choose `Run Task -> clean`).
180
210
 
181
211
  ### Developing the tool itself
182
212
 
183
213
  For a quick development loop it really helps linking the code in this repository as the global libdragon installation. To do this run;
184
214
 
185
- npm link
215
+ ```bash
216
+ npm link
217
+ ```
186
218
 
187
219
  in the root of the repository. Once you do this, running `libdragon` will use the code here rather than the actual npm installation. Then you can test your changes in the libdragon project here or elsewhere on your computer. This setup is automatically done if you use the [devcontainer](#experimental-devcontainer-support).
188
220
 
189
221
  When you are happy with your changes, you can verify you conform to the coding standards via:
190
222
 
191
- npm run format-check
192
- npm run lint-check
223
+ ```bash
224
+ npm run format-check
225
+ npm run lint-check
226
+ ```
193
227
 
194
228
  You can auto-fix applicable errors by running `format` and `lint` scripts instead. Additionally, typescript is used as the type system. To be able to get away with transpiling the code during development, jsDoc flavor of types are used instead of inline ones. To check your types, run:
195
229
 
196
- npm run tsc
230
+ ```bash
231
+ npm run tsc
232
+ ```
233
+
234
+ To run the test suite:
235
+
236
+ ```bash
237
+ npm run test
238
+ ```
197
239
 
198
240
  This repository uses [`semantic-release`](https://github.com/semantic-release/semantic-release) and manages releases from specially formatted commit messages. To simplify creating them you can use:
199
241
 
200
- npx cz
242
+ ```bash
243
+ npx cz
244
+ ```
201
245
 
202
246
  It will create a `semantic-release` compatible commit from your current staged changes.
203
247
 
@@ -9,6 +9,7 @@ const {
9
9
  initGitAndCacheContainerId,
10
10
  updateImage,
11
11
  runGitMaybeHost,
12
+ destroyContainer,
12
13
  } = require('./utils');
13
14
 
14
15
  const {
@@ -26,7 +27,6 @@ const {
26
27
  toPosixPath,
27
28
  toNativePath,
28
29
  } = require('../helpers');
29
- const { syncImageAndStart } = require('./update-and-start');
30
30
 
31
31
  /**
32
32
  * @param {import('../project-info').LibdragonInfo} info
@@ -226,13 +226,22 @@ async function init(info) {
226
226
  );
227
227
  if (!process.env.DOCKER_CONTAINER) {
228
228
  if (info.options.DOCKER_IMAGE) {
229
- info = await syncImageAndStart(info);
230
- } else {
229
+ // The logic here resembles syncImageAndStart but it aims at being idempotent
230
+ // w.r.t the container when called without any additional flag.
231
+ // Download the new image and if it is different, re-create the container
232
+ if (await updateImage(info, info.options.DOCKER_IMAGE)) {
233
+ await destroyContainer(info);
234
+ }
235
+
231
236
  info = {
232
237
  ...info,
233
- containerId: await start(info),
238
+ imageName: info.options.DOCKER_IMAGE,
234
239
  };
235
240
  }
241
+ info = {
242
+ ...info,
243
+ containerId: await start(info),
244
+ };
236
245
  }
237
246
 
238
247
  info = await autoVendor(info);
@@ -241,6 +250,12 @@ async function init(info) {
241
250
  }
242
251
 
243
252
  if (!process.env.DOCKER_CONTAINER) {
253
+ // We don't have a config yet, this is a fresh project, override with the
254
+ // image flag if provided. Also see https://github.com/anacierdem/libdragon-docker/issues/66
255
+ info = {
256
+ ...info,
257
+ imageName: info.options.DOCKER_IMAGE ?? info.imageName,
258
+ };
244
259
  // Download image and start it
245
260
  await updateImage(info, info.imageName);
246
261
  info.containerId = await start(info);
@@ -1,9 +1,5 @@
1
- const chalk = require('chalk').stderr;
2
-
3
1
  const { installDependencies } = require('./utils');
4
2
  const { start } = require('./start');
5
- const { syncImageAndStart } = require('./update-and-start');
6
- const { log } = require('../helpers');
7
3
 
8
4
  /**
9
5
  * Updates the image if flag is provided and install vendors onto the container.
@@ -15,22 +11,11 @@ const install = async (libdragonInfo) => {
15
11
  let updatedInfo = libdragonInfo;
16
12
 
17
13
  if (!process.env.DOCKER_CONTAINER) {
18
- const imageName = libdragonInfo.options.DOCKER_IMAGE;
19
- // If an image is provided, attempt to install
20
- if (imageName) {
21
- log(
22
- chalk.yellow(
23
- 'Using `install` action to update the docker image is deprecated. Use the `update` action instead.'
24
- )
25
- );
26
- updatedInfo = await syncImageAndStart(libdragonInfo);
27
- } else {
28
- // Make sure existing one is running
29
- updatedInfo = {
30
- ...updatedInfo,
31
- containerId: await start(libdragonInfo),
32
- };
33
- }
14
+ // Make sure existing one is running
15
+ updatedInfo = {
16
+ ...updatedInfo,
17
+ containerId: await start(libdragonInfo),
18
+ };
34
19
  }
35
20
 
36
21
  // Re-install vendors on new image
@@ -1,7 +1,48 @@
1
- const { log } = require('../helpers');
1
+ const { log, assert } = require('../helpers');
2
2
  const { LIBDRAGON_GIT, LIBDRAGON_BRANCH } = require('../constants');
3
- const { runGitMaybeHost, installDependencies } = require('./utils');
4
- const { syncImageAndStart } = require('./update-and-start');
3
+ const {
4
+ runGitMaybeHost,
5
+ installDependencies,
6
+ updateImage,
7
+ destroyContainer,
8
+ } = require('./utils');
9
+ const { start } = require('./start');
10
+
11
+ /**
12
+ * @param {import('../project-info').LibdragonInfo} libdragonInfo
13
+ */
14
+ async function syncImageAndStart(libdragonInfo) {
15
+ assert(
16
+ !process.env.DOCKER_CONTAINER,
17
+ new Error(
18
+ '[syncImageAndStart] We should already know we are in a container.'
19
+ )
20
+ );
21
+
22
+ const oldImageName = libdragonInfo.imageName;
23
+ const imageName = libdragonInfo.options.DOCKER_IMAGE ?? oldImageName;
24
+ // If an image is provided, always attempt to install it
25
+ // See https://github.com/anacierdem/libdragon-docker/issues/47
26
+ if (oldImageName !== imageName) {
27
+ log(`Updating image from \`${oldImageName}\` to \`${imageName}\``);
28
+ } else {
29
+ log(`Updating image \`${oldImageName}\``);
30
+ }
31
+
32
+ // Download the new image and if it is different, re-create the container
33
+ if (await updateImage(libdragonInfo, imageName)) {
34
+ await destroyContainer(libdragonInfo);
35
+ }
36
+
37
+ return {
38
+ ...libdragonInfo,
39
+ imageName,
40
+ containerId: await start({
41
+ ...libdragonInfo,
42
+ imageName,
43
+ }),
44
+ };
45
+ }
5
46
 
6
47
  /**
7
48
  * @param {import('../project-info').LibdragonInfo} info
@@ -21,6 +21,4 @@ module.exports = /** @type {const} */ ({
21
21
  STATUS_ERROR: 1,
22
22
  STATUS_BAD_PARAM: 2,
23
23
  STATUS_VALIDATION_ERROR: 4,
24
-
25
- IMAGE_FILE: 'docker-image', // deprecated
26
24
  });
@@ -121,7 +121,6 @@ const parseParameters = async (argv) => {
121
121
  !(
122
122
  /** @type {typeof actions[keyof actions][]} */ ([
123
123
  actions.init,
124
- actions.install,
125
124
  actions.update,
126
125
  ]).includes(options.CURRENT_ACTION)
127
126
  ) &&
@@ -14,7 +14,6 @@ const {
14
14
  CONFIG_FILE,
15
15
  DOCKER_HUB_IMAGE,
16
16
  DEFAULT_STRATEGY,
17
- IMAGE_FILE,
18
17
  LIBDRAGON_SUBMODULE,
19
18
  CACHED_CONTAINER_FILE,
20
19
  CONTAINER_TARGET_PATH,
@@ -76,6 +75,8 @@ async function findContainerId(libdragonInfo) {
76
75
  return id;
77
76
  }
78
77
 
78
+ // TODO: use docker container ls -a --format "{{.Labels}}" instead
79
+ // from what I remember this used to not work (i.e the property was not exposed before)
79
80
  const candidates = (
80
81
  await spawnProcess('docker', [
81
82
  'container',
@@ -159,15 +160,7 @@ const readProjectInfo = async function (optionInfo) {
159
160
  return /** @type {NoProjectInfo} */ (optionInfo);
160
161
  }
161
162
 
162
- const migratedRoot = await findLibdragonRoot();
163
-
164
- // Look for old one for backwards compatibility
165
- const projectRoot =
166
- migratedRoot ??
167
- (await findLibdragonRoot(
168
- '.',
169
- path.join(LIBDRAGON_PROJECT_MANIFEST, IMAGE_FILE)
170
- ));
163
+ const projectRoot = await findLibdragonRoot();
171
164
 
172
165
  if (
173
166
  !projectRoot &&
@@ -195,16 +188,13 @@ const readProjectInfo = async function (optionInfo) {
195
188
  // Only used for the init action ATM, and it is not ideal to have this here
196
189
  haveProjectConfig: !!projectRoot,
197
190
 
198
- // Set the defaults immediately, these should be present at all times even
199
- // if we are migrating from the old config because they did not exist before
200
- imageName: DOCKER_HUB_IMAGE,
201
191
  vendorDirectory: toPosixPath(path.join('.', LIBDRAGON_SUBMODULE)),
202
192
  vendorStrategy: DEFAULT_STRATEGY,
203
193
  };
204
194
 
205
195
  log(`Project root: ${info.root}`, true);
206
196
 
207
- if (migratedRoot) {
197
+ if (projectRoot) {
208
198
  info = {
209
199
  ...info,
210
200
  ...JSON.parse(
@@ -214,20 +204,6 @@ const readProjectInfo = async function (optionInfo) {
214
204
  )
215
205
  ),
216
206
  };
217
- } else {
218
- // Cleanup old files and migrate to the new config file
219
- const imageFile = path.join(
220
- info.root,
221
- LIBDRAGON_PROJECT_MANIFEST,
222
- IMAGE_FILE
223
- );
224
- if (await fileExists(imageFile)) {
225
- info.imageName = (
226
- await fs.readFile(imageFile, { encoding: 'utf8' })
227
- ).trim();
228
- // Immediately update the config as this is the first migration
229
- await Promise.all([writeProjectInfo(info), fs.rm(imageFile)]);
230
- }
231
207
  }
232
208
 
233
209
  if (!process.env.DOCKER_CONTAINER) {
@@ -235,10 +211,6 @@ const readProjectInfo = async function (optionInfo) {
235
211
  log(`Active container id: ${info.containerId}`, true);
236
212
  }
237
213
 
238
- // For imageName, flag has the highest priority followed by the one read from
239
- // the file and then if there is any matching container, name is read from it.
240
- // As last option fallback to default value.
241
-
242
214
  // If still have the container, read the image name from it
243
215
  // No need to do anything if we are in a container
244
216
  if (
@@ -258,6 +230,14 @@ const readProjectInfo = async function (optionInfo) {
258
230
  ).trim();
259
231
  }
260
232
 
233
+ // For imageName, flag has the highest priority followed by the one read from
234
+ // the file and then if there is any matching container, name is read from it.
235
+ // As last option fallback to default value.
236
+ // This represents the current value, so the flag should be processed later.
237
+ // If we were to update it here, it would be impossible to know the change
238
+ // with how we structure the code right now.
239
+ info.imageName = info.imageName ?? DOCKER_HUB_IMAGE;
240
+
261
241
  log(`Active image name: ${info.imageName}`, true);
262
242
  log(`Active vendor directory: ${info.vendorDirectory}`, true);
263
243
  log(`Active vendor strategy: ${info.vendorStrategy}`, true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "libdragon",
3
- "version": "10.9.1",
3
+ "version": "11.0.1",
4
4
  "description": "This is a docker wrapper for libdragon",
5
5
  "main": "index.js",
6
6
  "engines": {
@@ -11,6 +11,8 @@
11
11
  "libdragon": "./index.js"
12
12
  },
13
13
  "scripts": {
14
+ "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js",
15
+ "test:watch": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --watch",
14
16
  "libdragon": "node index.js",
15
17
  "start": "node index.js start",
16
18
  "stop": "node index.js stop",
@@ -50,10 +52,14 @@
50
52
  "cz-conventional-changelog": "^3.3.0",
51
53
  "ed64": "^2.0.4",
52
54
  "eslint": "^7.32.0",
55
+ "ffi-napi": "^4.0.3",
56
+ "jest": "^29.5.0",
53
57
  "pkg": "^5.5.2",
54
58
  "prettier": "^2.4.0",
55
- "semantic-release": "^19.0.2",
56
- "typescript": "^4.7.4"
59
+ "ref-napi": "^3.0.3",
60
+ "semantic-release": "^21.0.9",
61
+ "typescript": "^4.7.4",
62
+ "zx": "^7.2.2"
57
63
  },
58
64
  "overrides": {
59
65
  "minimist": "1.2.6"
@@ -1,43 +0,0 @@
1
- const { log, assert } = require('../helpers');
2
- const { updateImage, destroyContainer } = require('./utils');
3
- const { start } = require('./start');
4
-
5
- /**
6
- * @param {import('../project-info').LibdragonInfo} libdragonInfo
7
- */
8
- async function syncImageAndStart(libdragonInfo) {
9
- assert(
10
- !process.env.DOCKER_CONTAINER,
11
- new Error(
12
- '[syncImageAndStart] We should already know we are in a container.'
13
- )
14
- );
15
-
16
- const oldImageName = libdragonInfo.imageName;
17
- const imageName = libdragonInfo.options.DOCKER_IMAGE ?? oldImageName;
18
- // If an image is provided, always attempt to install it
19
- // See https://github.com/anacierdem/libdragon-docker/issues/47
20
- if (oldImageName !== imageName) {
21
- log(`Updating image from \`${oldImageName}\` to \`${imageName}\``);
22
- } else {
23
- log(`Updating image \`${oldImageName}\``);
24
- }
25
-
26
- // Download the new image and if it is different, re-create the container
27
- if (await updateImage(libdragonInfo, imageName)) {
28
- await destroyContainer(libdragonInfo);
29
- }
30
-
31
- return {
32
- ...libdragonInfo,
33
- imageName,
34
- containerId: await start({
35
- ...libdragonInfo,
36
- imageName,
37
- }),
38
- };
39
- }
40
-
41
- module.exports = {
42
- syncImageAndStart,
43
- };