@@ -20,76 +20,106 @@ 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 \/ / ] ;
27-
2827async function transformDir ( p ) {
28+ // We perform all command executions in parallel here to speed up.
29+ // Note that we can't parallelize for the full recursive directory,
30+ // as WSL and its interop would otherwise end up with some flaky errors.
31+ // See: https://github.com/microsoft/WSL/issues/8677.
32+ const tasks = [ ] ;
33+ // We explore directories after all files were checked at this level.
34+ const directoriesToVisit = [ ] ;
35+
2936 for ( const file of await fs . readdir ( p , { withFileTypes : true } ) ) {
3037 const subPath = path . join ( p , file . name ) ;
3138
3239 if ( skipDirectories . some ( ( d ) => subPath . endsWith ( d ) ) ) {
3340 continue ;
3441 }
3542
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- }
43+ if ( file . isSymbolicLink ( ) ) {
44+ // Allow for parallel processing of directory entries.
45+ tasks . push (
46+ ( async ( ) => {
47+ let target = '' ;
48+ try {
49+ target = await fs . realpath ( subPath ) ;
50+ } catch ( e ) {
51+ if ( debug ) {
52+ console . error ( 'Skipping' , subPath ) ;
53+ }
54+ return ;
55+ }
56+
57+ await fs . rm ( subPath ) ;
58+
59+ const subPathId = relativizeForSimilarWorkspacePaths ( subPath ) ;
60+ const targetPathId = relativizeForSimilarWorkspacePaths ( target ) ;
61+ const isSelfLink = subPathId === targetPathId ;
62+
63+ // This is an actual file that needs to be copied. Copy contents.
64+ // - the target path is equivalent to the link. This is a self-link from `.runfiles` to `bin/`.
65+ // - the target path is outside any of our workspace roots.
66+ if ( isSelfLink || targetPathId . startsWith ( '..' ) ) {
67+ await exec ( `cp -Rf ${ target } ${ subPath } ` ) ;
68+ return ;
69+ }
70+
71+ const relativeSubPath = relativizeToRoot ( subPath ) ;
72+ const targetAtDestination = path . relative ( path . dirname ( subPathId ) , targetPathId ) ;
73+ const targetAtDestinationWindowsPath = targetAtDestination . replace ( / \/ / g, '\\' ) ;
74+
75+ const wslSubPath = relativeSubPath . replace ( / \/ / g, '\\' ) ;
76+
77+ if ( debug ) {
78+ console . log ( {
79+ targetAtDestination,
80+ subPath,
81+ relativeSubPath,
82+ target,
83+ targetPathId,
84+ subPathId,
85+ } ) ;
86+ }
87+
88+ if ( ( await fs . stat ( target ) ) . isDirectory ( ) ) {
89+ // A symlink to a directory, create a dir junction.
90+ await exec (
91+ `${ cmdPath } /c mklink /d "${ wslSubPath } " "${ targetAtDestinationWindowsPath } "` ,
92+ ) ;
93+ } else {
94+ // A symlink to a file, create a file junction.
95+ await exec ( `${ cmdPath } /c mklink "${ wslSubPath } " "${ targetAtDestinationWindowsPath } "` ) ;
96+ }
97+ } ) ( ) ,
98+ ) ;
99+ } else if ( file . isDirectory ( ) ) {
100+ directoriesToVisit . push ( subPath ) ;
101+ }
102+ }
62103
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- }
104+ // Wait for all commands/tasks to complete, executed in parallel.
105+ await Promise . all ( tasks ) ;
79106
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- }
107+ // Descend into other directories, sequentially to avoid WSL interop errors.
108+ for ( const d of directoriesToVisit ) {
109+ await transformDir ( d ) ;
88110 }
89111}
90112
91113function exec ( cmd ) {
92- childProcess . execSync ( cmd , { cwd : rootDir } ) ;
114+ return new Promise ( ( resolve , reject ) => {
115+ childProcess . exec ( cmd , { cwd : rootDir } , ( error ) => {
116+ if ( error !== null ) {
117+ reject ( error ) ;
118+ } else {
119+ resolve ( ) ;
120+ }
121+ } ) ;
122+ } ) ;
93123}
94124
95125function relativizeForSimilarWorkspacePaths ( p ) {
@@ -110,4 +140,9 @@ function relativizeToRoot(p) {
110140 throw new Error ( 'Could not relativize to root: ' + p ) ;
111141}
112142
113- await transformDir ( rootDir ) ;
143+ try {
144+ await transformDir ( rootDir ) ;
145+ } catch ( err ) {
146+ console . error ( 'Could not convert symlinks:' , err ) ;
147+ process . exitCode = 1 ;
148+ }
0 commit comments