libdragon 11.0.0 → 11.0.2

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
@@ -54,7 +54,7 @@ Download the [pre-built executable](https://github.com/anacierdem/libdragon-dock
54
54
  Install [node.js](https://nodejs.org/en/download/) (`>= 18`) and install `libdragon` as a global NPM package;
55
55
 
56
56
  ```bash
57
- npm install -g libdragon
57
+ npm install -g libdragon
58
58
  ```
59
59
 
60
60
  To update the tool to the latest, do `npm i -g libdragon@latest`.
@@ -64,23 +64,23 @@ To update the tool to the latest, do `npm i -g libdragon@latest`.
64
64
  Navigate to the folder you want to initialize your project and invoke libdragon;
65
65
 
66
66
  ```bash
67
- libdragon init
67
+ libdragon init
68
68
  ```
69
69
 
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` 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.
71
71
 
72
72
  The container's `make` can be invoked on your current working directory via;
73
73
 
74
- ```
75
- libdragon make
74
+ ```bash
75
+ libdragon make
76
76
  ```
77
77
 
78
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.
79
79
 
80
80
  To update the library and rebuild/install all the artifacts;
81
81
 
82
- ```
83
- libdragon update
82
+ ```bash
83
+ libdragon update
84
84
  ```
85
85
 
86
86
  In general, you can invoke libdragon as follows;
@@ -96,20 +96,20 @@ Run `libdragon help [action]` for more details on individual actions.
96
96
  Initialize your project as usual:
97
97
 
98
98
  ```bash
99
- libdragon init
99
+ libdragon init
100
100
  ```
101
101
 
102
102
  Then switch the submodule to the desired branch:
103
103
 
104
104
  ```bash
105
- git -C ./libdragon checkout opengl
106
- libdragon install
105
+ git -C ./libdragon checkout opengl
106
+ libdragon install
107
107
  ```
108
108
 
109
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
110
 
111
111
  ```bash
112
- git submodule set-branch -b opengl libdragon
112
+ git submodule set-branch -b opengl libdragon
113
113
  ```
114
114
 
115
115
  This will update the branch on `.gitmodules` and if you commit that change, subsequent initializations will use the `opengl` branch by default.
@@ -119,19 +119,16 @@ This will update the branch on `.gitmodules` and if you commit that change, subs
119
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:
120
120
 
121
121
  ```bash
122
- libdragon install
122
+ libdragon install
123
123
  ```
124
124
 
125
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.
126
126
 
127
- 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:
128
128
 
129
129
  ```makefile
130
- libdragon-install: libdragon
130
+ libdragon-install:
131
131
  $(MAKE) -C ./libdragon install
132
-
133
- libdragon:
134
- $(MAKE) -C ./libdragon
135
132
  ```
136
133
 
137
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.
@@ -153,19 +150,19 @@ To be able to share your project with the library change, you just commit your c
153
150
  After cloning this repository on a system with node.js (`>= 18`) & docker (`>= 24`), in this repository's root do;
154
151
 
155
152
  ```bash
156
- npm install
153
+ npm install
157
154
  ```
158
155
 
159
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`)
160
157
 
161
158
  ```bash
162
- git submodule update --init
159
+ git submodule update --init
163
160
  ```
164
161
 
165
162
  Then run;
166
163
 
167
164
  ```bash
168
- npm run libdragon -- init
165
+ npm run libdragon -- init
169
166
  ```
170
167
 
171
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.
@@ -175,23 +172,22 @@ Now you will be able to work on the files simultaneously with the docker contain
175
172
  There is a root `Makefile` making deeper makefiles easier with these recipes;
176
173
 
177
174
  bench: build the test bench (see below)
178
- examples: re-build libdragon examples
179
- tests: re-build the test ROM
180
- libdragon: build libdragon itself
181
- libdragon-install: install libdragon
175
+ examples: build libdragon examples
176
+ tests: build the test ROM
177
+ libdragon-install: build and install libdragon
182
178
  clean-bench: clean the test bench (see below)
183
179
  clean: clean everything and start from scratch
184
180
 
185
181
  For example, to re-build the original libdragon examples do;
186
182
 
187
183
  ```bash
188
- npm run libdragon -- make examples
184
+ npm run libdragon -- make examples
189
185
  ```
190
186
 
191
187
  Similarly to run the `clean` recipe, run;
192
188
 
193
189
  ```bash
194
- npm run libdragon -- make clean
190
+ npm run libdragon -- make clean
195
191
  ```
196
192
 
197
193
  Keep in mind that `--` is necessary for actual arguments when using npm scripts.
@@ -199,21 +195,25 @@ Keep in mind that `--` is necessary for actual arguments when using npm scripts.
199
195
  To update the submodule and re-build everything;
200
196
 
201
197
  ```bash
202
- npm run libdragon -- update
198
+ npm run libdragon -- update
203
199
  ```
204
200
 
205
201
  ### Local test bench
206
202
 
207
- 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.
204
+
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
208
 
209
- 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`).
209
+ You can clean everything with the `clean` recipe/task (open the command palette and choose `Run Task -> clean`).
210
210
 
211
211
  ### Developing the tool itself
212
212
 
213
213
  For a quick development loop it really helps linking the code in this repository as the global libdragon installation. To do this run;
214
214
 
215
215
  ```bash
216
- npm link
216
+ npm link
217
217
  ```
218
218
 
219
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).
@@ -221,26 +221,26 @@ in the root of the repository. Once you do this, running `libdragon` will use th
221
221
  When you are happy with your changes, you can verify you conform to the coding standards via:
222
222
 
223
223
  ```bash
224
- npm run format-check
225
- npm run lint-check
224
+ npm run format-check
225
+ npm run lint-check
226
226
  ```
227
227
 
228
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:
229
229
 
230
230
  ```bash
231
- npm run tsc
231
+ npm run tsc
232
232
  ```
233
233
 
234
234
  To run the test suite:
235
235
 
236
236
  ```bash
237
- npm run test
237
+ npm run test
238
238
  ```
239
239
 
240
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:
241
241
 
242
242
  ```bash
243
- npx cz
243
+ npx cz
244
244
  ```
245
245
 
246
246
  It will create a `semantic-release` compatible commit from your current staged changes.
@@ -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);
@@ -274,7 +289,7 @@ module.exports = /** @type {const} */ ({
274
289
  usage: {
275
290
  name: 'init',
276
291
  summary: 'Create a libdragon project in the current directory.',
277
- 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.
292
+ 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. When running in a submodule, initializion will take place at that level rather than the superproject. Do not remove the \`.libdragon\` folder and commit its contents if you are using source control, as it keeps persistent libdragon project information.
278
293
 
279
294
  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.
280
295
 
@@ -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
@@ -242,7 +242,19 @@ async function initGitAndCacheContainerId(libdragonInfo) {
242
242
  await runGitMaybeHost(libdragonInfo, ['init']);
243
243
  }
244
244
 
245
- const gitFolder = path.join(libdragonInfo.root, '.git');
245
+ const rootGitFolder = (
246
+ await spawnProcess('git', [
247
+ 'rev-parse',
248
+ '--show-superproject-working-tree',
249
+ ]).catch(() => {
250
+ // Probably host does not have git, can ignore
251
+ return '';
252
+ })
253
+ ).trim();
254
+
255
+ // Fallback to the potential git root on the project root if there is no parent
256
+ // git project.
257
+ const gitFolder = path.join(rootGitFolder || libdragonInfo.root, '.git');
246
258
  if (await dirExists(gitFolder)) {
247
259
  await fs.writeFile(
248
260
  path.join(gitFolder, CACHED_CONTAINER_FILE),
@@ -75,6 +75,8 @@ async function findContainerId(libdragonInfo) {
75
75
  return id;
76
76
  }
77
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)
78
80
  const candidates = (
79
81
  await spawnProcess('docker', [
80
82
  'container',
@@ -186,9 +188,6 @@ const readProjectInfo = async function (optionInfo) {
186
188
  // Only used for the init action ATM, and it is not ideal to have this here
187
189
  haveProjectConfig: !!projectRoot,
188
190
 
189
- // Set the defaults immediately, these should be present at all times even
190
- // if we are migrating from the old config because they did not exist before
191
- imageName: DOCKER_HUB_IMAGE,
192
191
  vendorDirectory: toPosixPath(path.join('.', LIBDRAGON_SUBMODULE)),
193
192
  vendorStrategy: DEFAULT_STRATEGY,
194
193
  };
@@ -212,10 +211,6 @@ const readProjectInfo = async function (optionInfo) {
212
211
  log(`Active container id: ${info.containerId}`, true);
213
212
  }
214
213
 
215
- // For imageName, flag has the highest priority followed by the one read from
216
- // the file and then if there is any matching container, name is read from it.
217
- // As last option fallback to default value.
218
-
219
214
  // If still have the container, read the image name from it
220
215
  // No need to do anything if we are in a container
221
216
  if (
@@ -235,6 +230,14 @@ const readProjectInfo = async function (optionInfo) {
235
230
  ).trim();
236
231
  }
237
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
+
238
241
  log(`Active image name: ${info.imageName}`, true);
239
242
  log(`Active vendor directory: ${info.vendorDirectory}`, true);
240
243
  log(`Active vendor strategy: ${info.vendorStrategy}`, true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "libdragon",
3
- "version": "11.0.0",
3
+ "version": "11.0.2",
4
4
  "description": "This is a docker wrapper for libdragon",
5
5
  "main": "index.js",
6
6
  "engines": {
@@ -12,6 +12,7 @@
12
12
  },
13
13
  "scripts": {
14
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",
15
16
  "libdragon": "node index.js",
16
17
  "start": "node index.js start",
17
18
  "stop": "node index.js stop",
@@ -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
- };