@@ -20,12 +20,19 @@ 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
@@ -34,62 +41,85 @@ async function transformDir(p) {
3441 }
3542
3643 if ( file . isDirectory ( ) ) {
37- await transformDir ( subPath ) ;
44+ directoriesToVisit . push ( subPath ) ;
3845 } 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- }
46+ // Allow for parallel processing of directory entries.
47+ tasks . push (
48+ ( async ( ) => {
49+ let target = '' ;
50+ try {
51+ target = await fs . realpath ( subPath ) ;
52+ } catch ( e ) {
53+ if ( debug ) {
54+ console . error ( 'Skipping' , subPath ) ;
55+ }
56+ return ;
57+ }
58+
59+ await fs . rm ( subPath ) ;
60+
61+ const subPathId = relativizeForSimilarWorkspacePaths ( subPath ) ;
62+ const targetPathId = relativizeForSimilarWorkspacePaths ( target ) ;
63+ const isSelfLink = subPathId === targetPathId ;
64+
65+ // This is an actual file that needs to be copied. Copy contents.
66+ // - the target path is equivalent to the link. This is a self-link from `.runfiles` to `bin/`.
67+ // - the target path is outside any of our workspace roots.
68+ if ( isSelfLink || targetPathId . startsWith ( '..' ) ) {
69+ await exec ( `cp -Rf ${ target } ${ subPath } ` ) ;
70+ return ;
71+ }
72+
73+ const relativeSubPath = relativizeToRoot ( subPath ) ;
74+ const targetAtDestination = path . relative ( path . dirname ( subPathId ) , targetPathId ) ;
75+ const targetAtDestinationWindowsPath = targetAtDestination . replace ( / \/ / g, '\\' ) ;
76+
77+ const wslSubPath = relativeSubPath . replace ( / \/ / g, '\\' ) ;
78+
79+ if ( debug ) {
80+ console . log ( {
81+ targetAtDestination,
82+ subPath,
83+ relativeSubPath,
84+ target,
85+ targetPathId,
86+ subPathId,
87+ } ) ;
88+ }
89+
90+ if ( ( await fs . stat ( target ) ) . isDirectory ( ) ) {
91+ // A symlink to a directory, create a dir junction.
92+ await exec (
93+ `${ cmdPath } /c mklink /d "${ wslSubPath } " "${ targetAtDestinationWindowsPath } "` ,
94+ ) ;
95+ } else {
96+ // A symlink to a file, create a file junction.
97+ await exec ( `${ cmdPath } /c mklink "${ wslSubPath } " "${ targetAtDestinationWindowsPath } "` ) ;
98+ }
99+ } ) ( ) ,
100+ ) ;
101+ }
62102
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- }
103+ // Wait for all commands/tasks to complete, executed in parallel.
104+ await Promise . all ( tasks ) ;
79105
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- }
106+ // Descend into other directories, sequentially to avoid WSL interop errors.
107+ for ( const dirPath of directoriesToVisit ) {
108+ await transformDir ( dirPath ) ;
87109 }
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