@@ -20,76 +20,113 @@ const [rootDir, cmdPath] = process.argv.slice(2);
20
20
const debug = process . env . DEBUG === '1' ;
21
21
const skipDirectories = [
22
22
// Modules that we don't need and would unnecessarily slow-down this.
23
- 'node22_windows_amd64 /bin/nodejs/node_modules' ,
23
+ '_windows_amd64 /bin/nodejs/node_modules' ,
24
24
] ;
25
25
26
26
const workspaceRootPaths = [ / .* \. r u n f i l e s \/ a n g u l a r _ c l i \/ / , / ^ .* - f a s t b u i l d \/ b i n \/ / ] ;
27
27
28
+ const executedCommands = [ ] ;
29
+
30
+ // Copying can be parallelized and doesn't cause any WSL flakiness (no exe is invoked).
31
+ const parallelCopyTasks = [ ] ;
32
+
28
33
async function transformDir ( p ) {
34
+ // We perform all command executions in parallel here to speed up.
35
+ // Note that we can't parallelize for the full recursive directory,
36
+ // as WSL and its interop would otherwise end up with some flaky errors.
37
+ // See: https://github.com/microsoft/WSL/issues/8677.
38
+ const tasks = [ ] ;
39
+ // We explore directories after all files were checked at this level.
40
+ const directoriesToVisit = [ ] ;
41
+
29
42
for ( const file of await fs . readdir ( p , { withFileTypes : true } ) ) {
30
43
const subPath = path . join ( p , file . name ) ;
31
44
32
45
if ( skipDirectories . some ( ( d ) => subPath . endsWith ( d ) ) ) {
33
46
continue ;
34
47
}
35
48
36
- if ( file . isDirectory ( ) ) {
37
- await transformDir ( subPath ) ;
38
- } else if ( file . isSymbolicLink ( ) ) {
39
- let target = '' ;
40
- try {
41
- target = await fs . realpath ( subPath ) ;
42
- } catch ( e ) {
43
- if ( debug ) {
44
- console . error ( 'Skipping' , subPath ) ;
45
- }
46
- continue ;
47
- }
48
-
49
- await fs . rm ( subPath ) ;
50
-
51
- const subPathId = relativizeForSimilarWorkspacePaths ( subPath ) ;
52
- const targetPathId = relativizeForSimilarWorkspacePaths ( target ) ;
53
- const isSelfLink = subPathId === targetPathId ;
54
-
55
- // This is an actual file that needs to be copied. Copy contents.
56
- // - the target path is outside any of our workspace roots.
57
- // - the target path is equivalent to the link. This is a self-link from `.runfiles` to `bin/`.
58
- if ( isSelfLink || targetPathId . startsWith ( '..' ) ) {
59
- exec ( `cp -Rf ${ target } ${ subPath } ` ) ;
60
- continue ;
61
- }
49
+ if ( file . isSymbolicLink ( ) ) {
50
+ // Allow for parallel processing of directory entries.
51
+ tasks . push (
52
+ ( async ( ) => {
53
+ let target = '' ;
54
+ try {
55
+ target = await fs . realpath ( subPath ) ;
56
+ } catch ( e ) {
57
+ if ( debug ) {
58
+ console . error ( 'Skipping' , subPath ) ;
59
+ }
60
+ return ;
61
+ }
62
+
63
+ await fs . rm ( subPath ) ;
64
+
65
+ const subPathId = relativizeForSimilarWorkspacePaths ( subPath ) ;
66
+ const targetPathId = relativizeForSimilarWorkspacePaths ( target ) ;
67
+ const isSelfLink = subPathId === targetPathId ;
68
+
69
+ // This is an actual file that needs to be copied. Copy contents.
70
+ // - the target path is equivalent to the link. This is a self-link from `.runfiles` to `bin/`.
71
+ // - the target path is outside any of our workspace roots.
72
+ if ( isSelfLink || targetPathId . startsWith ( '..' ) ) {
73
+ parallelCopyTasks . push ( exec ( `cp -Rf ${ target } ${ subPath } ` ) ) ;
74
+ return ;
75
+ }
76
+
77
+ const relativeSubPath = relativizeToRoot ( subPath ) ;
78
+ const targetAtDestination = path . relative ( path . dirname ( subPathId ) , targetPathId ) ;
79
+ const targetAtDestinationWindowsPath = targetAtDestination . replace ( / \/ / g, '\\' ) ;
80
+
81
+ const wslSubPath = relativeSubPath . replace ( / \/ / g, '\\' ) ;
82
+
83
+ if ( debug ) {
84
+ console . log ( {
85
+ targetAtDestination,
86
+ subPath,
87
+ relativeSubPath,
88
+ target,
89
+ targetPathId,
90
+ subPathId,
91
+ } ) ;
92
+ }
93
+
94
+ if ( ( await fs . stat ( target ) ) . isDirectory ( ) ) {
95
+ // A symlink to a directory, create a dir junction.
96
+ await exec (
97
+ `${ cmdPath } /c mklink /d "${ wslSubPath } " "${ targetAtDestinationWindowsPath } "` ,
98
+ ) ;
99
+ } else {
100
+ // A symlink to a file, create a file junction.
101
+ await exec ( `${ cmdPath } /c mklink "${ wslSubPath } " "${ targetAtDestinationWindowsPath } "` ) ;
102
+ }
103
+ } ) ( ) ,
104
+ ) ;
105
+ } else if ( file . isDirectory ( ) ) {
106
+ directoriesToVisit . push ( subPath ) ;
107
+ }
108
+ }
62
109
63
- const relativeSubPath = relativizeToRoot ( subPath ) ;
64
- const targetAtDestination = path . relative ( path . dirname ( subPathId ) , targetPathId ) ;
65
- const targetAtDestinationWindowsPath = targetAtDestination . replace ( / \/ / g, '\\' ) ;
66
-
67
- const wslSubPath = relativeSubPath . replace ( / \/ / g, '\\' ) ;
68
-
69
- if ( debug ) {
70
- console . log ( {
71
- targetAtDestination,
72
- subPath,
73
- relativeSubPath,
74
- target,
75
- targetPathId,
76
- subPathId,
77
- } ) ;
78
- }
110
+ // Wait for all commands/tasks to complete, executed in parallel.
111
+ await Promise . all ( tasks ) ;
79
112
80
- if ( ( await fs . stat ( target ) ) . isDirectory ( ) ) {
81
- // A symlink to a directory, create a dir junction.
82
- exec ( `${ cmdPath } /c mklink /d "${ wslSubPath } " "${ targetAtDestinationWindowsPath } "` ) ;
83
- } else {
84
- // A symlink to a file, create a file junction.
85
- exec ( `${ cmdPath } /c mklink "${ wslSubPath } " "${ targetAtDestinationWindowsPath } "` ) ;
86
- }
87
- }
113
+ // Descend into other directories, sequentially to avoid WSL interop errors.
114
+ for ( const d of directoriesToVisit ) {
115
+ await transformDir ( d ) ;
88
116
}
89
117
}
90
118
91
119
function exec ( cmd ) {
92
- childProcess . execSync ( cmd , { cwd : rootDir } ) ;
120
+ return new Promise ( ( resolve , reject ) => {
121
+ childProcess . exec ( cmd , { cwd : rootDir } , ( error ) => {
122
+ if ( error !== null ) {
123
+ reject ( error ) ;
124
+ } else {
125
+ executedCommands . push ( cmd ) ;
126
+ resolve ( ) ;
127
+ }
128
+ } ) ;
129
+ } ) ;
93
130
}
94
131
95
132
function relativizeForSimilarWorkspacePaths ( p ) {
@@ -110,4 +147,12 @@ function relativizeToRoot(p) {
110
147
throw new Error ( 'Could not relativize to root: ' + p ) ;
111
148
}
112
149
113
- await transformDir ( rootDir ) ;
150
+ try {
151
+ await transformDir ( rootDir ) ;
152
+ await Promise . all ( parallelCopyTasks ) ;
153
+
154
+ console . error ( 'Executed commands' , executedCommands ) ;
155
+ } catch ( err ) {
156
+ console . error ( 'Could not convert symlinks:' , err ) ;
157
+ process . exitCode = 1 ;
158
+ }
0 commit comments