Skip to content

Commit dfd1b2d

Browse files
committed
🦄 refactor: Convert patch node build script
1 parent ba615f7 commit dfd1b2d

File tree

7 files changed

+251
-148
lines changed

7 files changed

+251
-148
lines changed

.github/workflows/linux_x86_64_build.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ jobs:
134134
with:
135135
python-version: '3.11'
136136

137+
- name: Setup Deno
138+
uses: denoland/setup-deno@v2
139+
with:
140+
deno-version: v2.x
141+
137142
- name: Build Node.js
138143
run: |
139144
cd ${{ env.ROOT }}
@@ -142,9 +147,9 @@ jobs:
142147
git checkout v${{ env.JAVET_NODE_VERSION }}
143148
sed -i 's/__attribute__((tls_model(V8_TLS_MODEL)))/ /g' deps/v8/src/execution/isolate.h
144149
sed -i 's/__attribute__((tls_model(V8_TLS_MODEL)))/ /g' deps/v8/src/heap/local-heap.h
145-
python3 ${{ env.ROOT }}/Javet/scripts/python/patch_node_build.py -p ./
150+
deno --allow-all ${{ env.ROOT }}/Javet/scripts/deno/patch_node_build.ts -p ./
146151
./configure --enable-static ${{ matrix.node_flag }}
147-
python3 ${{ env.ROOT }}/Javet/scripts/python/patch_node_build.py -p ./
152+
deno --allow-all ${{ env.ROOT }}/Javet/scripts/deno/patch_node_build.ts -p ./
148153
make -j4
149154
mv out out.${{ matrix.name }}
150155

scripts/deno/change_javet_version.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@
1616
*/
1717

1818
// deno-lint-ignore-file no-regex-spaces
19-
import { dirname, fromFileUrl, resolve } from "@std/path";
19+
import * as path from "@std/path";
2020

2121
class ChangeJavetVersion {
2222
private rootPath: string;
2323
private version: string;
2424

2525
constructor(version: string) {
26-
const scriptPath = fromFileUrl(import.meta.url);
27-
this.rootPath = resolve(dirname(scriptPath), "../../");
26+
const scriptPath = path.fromFileUrl(import.meta.url);
27+
this.rootPath = path.resolve(path.dirname(scriptPath), "../../");
2828
this.version = version;
2929
}
3030

@@ -236,7 +236,7 @@ class ChangeJavetVersion {
236236
lineSeparator: string,
237237
...patterns: RegExp[]
238238
): void {
239-
const filePath = resolve(this.rootPath, relativeFilePath);
239+
const filePath = path.resolve(this.rootPath, relativeFilePath);
240240
console.log(`INFO: Updating ${filePath}.`);
241241

242242
const lines: string[] = [];

scripts/deno/change_node_v8_version.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { dirname, fromFileUrl, resolve } from "@std/path";
18+
import * as path from "@std/path";
1919

2020
abstract class ChangeVersion {
2121
protected rootPath: string;
2222
protected version: string;
2323

2424
constructor(version: string) {
25-
const scriptPath = fromFileUrl(import.meta.url);
26-
this.rootPath = resolve(dirname(scriptPath), "../../");
25+
const scriptPath = path.fromFileUrl(import.meta.url);
26+
this.rootPath = path.resolve(path.dirname(scriptPath), "../../");
2727
this.version = version;
2828
}
2929

@@ -34,7 +34,7 @@ abstract class ChangeVersion {
3434
lineSeparator: string,
3535
...patterns: RegExp[]
3636
): void {
37-
const filePath = resolve(this.rootPath, relativeFilePath);
37+
const filePath = path.resolve(this.rootPath, relativeFilePath);
3838
console.log(`INFO: Updating ${filePath}.`);
3939

4040
const lines: string[] = [];

scripts/deno/deno.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
{
22
"tasks": {
3-
"dev": "deno run --watch main.ts"
43
},
54
"imports": {
65
"@std/assert": "jsr:@std/assert@1",
6+
"@std/cli": "jsr:@std/cli@^1.0.6",
7+
"@std/fs": "jsr:@std/fs@^1.0.4",
78
"@std/path": "jsr:@std/path@^1.0.8"
89
}
910
}

scripts/deno/deno.lock

Lines changed: 23 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/deno/patch_node_build.ts

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/*
2+
* Copyright (c) 2021-2025. caoccao.com Sam Cao
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/*
19+
* This TypeScript script is for patching Node.js on Linux.
20+
*
21+
* 1. Clone Node.js.
22+
* 2. Checkout to proper version.
23+
* 3. Run the script: `deno run --allow-read --allow-write patch_node_build.ts -p root_path_to_node_js`
24+
* 4. Run `./configure --enable-static --without-intl` under `root_path_to_node_js`.
25+
* 5. Run the script again: `deno run --allow-read --allow-write patch_node_build.ts -p root_path_to_node_js`
26+
* 6. Run `make -j4` under `root_path_to_node_js`.
27+
*/
28+
29+
import * as cli from "@std/cli";
30+
import * as path from "@std/path";
31+
32+
class PatchNodeBuild {
33+
private escape = "\\";
34+
private lineSeparator = "\n";
35+
36+
// Patch ./common.gypi to generate arch static libraries.
37+
private commonFile = "common.gypi";
38+
private commonOldKey = '_type=="static_library" and OS=="solaris"';
39+
private commonNewKey = '_type=="static_library"';
40+
41+
// Patch make files to generate position independent code.
42+
private makeKeys = [
43+
"CFLAGS_Release :=",
44+
"CFLAGS_C_Release :=",
45+
"CFLAGS_CC_Release :=",
46+
"LDFLAGS_Release :=",
47+
];
48+
private makeProperty =
49+
" -fPIC -ftls-model=global-dynamic -Wno-return-type \\";
50+
private makePropertyInline =
51+
" -fPIC -ftls-model=global-dynamic -Wno-return-type ";
52+
53+
private nodeRepoPath: string;
54+
55+
constructor(nodeRepoPath: string) {
56+
this.nodeRepoPath = path.resolve(nodeRepoPath);
57+
}
58+
59+
private patchCommon(): void {
60+
const filePath = path.resolve(this.nodeRepoPath, this.commonFile);
61+
62+
try {
63+
const stat = Deno.statSync(filePath);
64+
if (!stat.isFile) {
65+
console.error(`ERROR: Failed to locate ${filePath}.`);
66+
return;
67+
}
68+
69+
const originalBuffer = Deno.readFileSync(filePath);
70+
const decoder = new TextDecoder("utf-8");
71+
const content = decoder.decode(originalBuffer);
72+
73+
const lines: string[] = [];
74+
for (const line of content.split(this.lineSeparator)) {
75+
let updatedLine = line;
76+
if (line.includes(this.commonOldKey)) {
77+
updatedLine = line.replace(this.commonOldKey, this.commonNewKey);
78+
}
79+
lines.push(updatedLine);
80+
}
81+
82+
const encoder = new TextEncoder();
83+
const newBuffer = encoder.encode(lines.join(this.lineSeparator));
84+
85+
if (this.buffersEqual(originalBuffer, newBuffer)) {
86+
console.warn(`WARN: Skipped ${filePath}.`);
87+
} else {
88+
Deno.writeFileSync(filePath, newBuffer);
89+
console.log(`INFO: Patched ${filePath}.`);
90+
}
91+
} catch (_error) {
92+
console.error(`ERROR: Failed to locate ${filePath}.`);
93+
}
94+
}
95+
96+
private async *walkDir(dir: string): AsyncGenerator<string> {
97+
try {
98+
for await (const entry of Deno.readDir(dir)) {
99+
const entryPath = path.resolve(dir, entry.name);
100+
if (entry.isDirectory) {
101+
yield* this.walkDir(entryPath);
102+
} else if (entry.isFile && entry.name.endsWith(".mk")) {
103+
yield entryPath;
104+
}
105+
}
106+
} catch {
107+
// Directory doesn't exist or can't be read
108+
return;
109+
}
110+
}
111+
112+
private async patchMakeFiles(): Promise<void> {
113+
const outDir = path.resolve(this.nodeRepoPath, "out");
114+
115+
for await (const filePath of this.walkDir(outDir)) {
116+
try {
117+
const stat = Deno.statSync(filePath);
118+
if (!stat.isFile) {
119+
continue;
120+
}
121+
122+
const originalBuffer = Deno.readFileSync(filePath);
123+
const decoder = new TextDecoder("utf-8");
124+
const content = decoder.decode(originalBuffer);
125+
126+
const lines: string[] = [];
127+
let patchRequired = false;
128+
129+
for (const line of content.split(this.lineSeparator)) {
130+
if (patchRequired) {
131+
patchRequired = false;
132+
if (line !== this.makeProperty) {
133+
lines.push(this.makeProperty);
134+
}
135+
}
136+
137+
let updatedLine = line;
138+
for (const makeKey of this.makeKeys) {
139+
if (line.startsWith(makeKey)) {
140+
if (!line.endsWith(this.escape)) {
141+
if (!line.endsWith(this.makePropertyInline)) {
142+
updatedLine += this.makePropertyInline;
143+
}
144+
} else {
145+
patchRequired = true;
146+
}
147+
break;
148+
}
149+
}
150+
151+
lines.push(updatedLine);
152+
}
153+
154+
const encoder = new TextEncoder();
155+
const newBuffer = encoder.encode(lines.join(this.lineSeparator));
156+
157+
if (this.buffersEqual(originalBuffer, newBuffer)) {
158+
console.warn(`WARN: Skipped ${filePath}.`);
159+
} else {
160+
Deno.writeFileSync(filePath, newBuffer);
161+
console.log(`INFO: Patched ${filePath}.`);
162+
}
163+
} catch {
164+
// Skip files that can't be read
165+
continue;
166+
}
167+
}
168+
}
169+
170+
private buffersEqual(a: Uint8Array, b: Uint8Array): boolean {
171+
if (a.length !== b.length) return false;
172+
for (let i = 0; i < a.length; i++) {
173+
if (a[i] !== b[i]) return false;
174+
}
175+
return true;
176+
}
177+
178+
async patch(): Promise<number> {
179+
this.patchCommon();
180+
await this.patchMakeFiles();
181+
return 0;
182+
}
183+
}
184+
185+
async function main(): Promise<number> {
186+
if (Deno.build.os !== "linux") {
187+
console.error("ERROR: This script is for Linux only.");
188+
return 1;
189+
}
190+
191+
const args = cli.parseArgs(Deno.args, {
192+
string: ["path", "p"],
193+
alias: { p: "path" },
194+
});
195+
196+
const nodeRepoPath = args.path || args.p;
197+
if (!nodeRepoPath) {
198+
console.error("ERROR: Missing required argument: -p/--path");
199+
console.error(
200+
"Usage: deno run --allow-read --allow-write patch_node_build.ts -p <node_repo_path>",
201+
);
202+
return 1;
203+
}
204+
205+
const patchNodeBuild = new PatchNodeBuild(nodeRepoPath as string);
206+
return await patchNodeBuild.patch();
207+
}
208+
209+
if (import.meta.main) {
210+
Deno.exit(await main());
211+
}

0 commit comments

Comments
 (0)