@@ -20,76 +20,113 @@ const [rootDir, cmdPath] = process.argv.slice(2);
2020const debug = process . env . DEBUG === '1' ;
2121const skipDirectories = [
2222 // 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' ,
2424] ;
2525
2626const 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 \/ / ] ;
2727
28+ const executedCommands = [ ] ;
29+
30+ // Copying can be parallelized and doesn't cause any WSL flakiness (no exe is invoked).
31+ const parallelCopyTasks = [ ] ;
32+
2833async 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+
2942 for ( const file of await fs . readdir ( p , { withFileTypes : true } ) ) {
3043 const subPath = path . join ( p , file . name ) ;
3144
3245 if ( skipDirectories . some ( ( d ) => subPath . endsWith ( d ) ) ) {
3346 continue ;
3447 }
3548
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+ }
62109
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 ) ;
79112
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 ) ;
88116 }
89117}
90118
91119function 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+ } ) ;
93130}
94131
95132function relativizeForSimilarWorkspacePaths ( p ) {
@@ -110,4 +147,12 @@ function relativizeToRoot(p) {
110147 throw new Error ( 'Could not relativize to root: ' + p ) ;
111148}
112149
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