diff --git a/config/scripts/ensure-native-runtime.mjs b/config/scripts/ensure-native-runtime.mjs index 3d701b23944..bee1b3fed9e 100644 --- a/config/scripts/ensure-native-runtime.mjs +++ b/config/scripts/ensure-native-runtime.mjs @@ -13,6 +13,7 @@ import { windowsProcessTreeAddonPath } from './windows-process-tree-gyp-rebuild.mjs' import { describeProcessFailure, runProcessSync } from './script-child-process.mjs' +import { disableMsbuildFileTrackingOnWindows } from './msbuild-file-tracking.mjs' const require = createRequire(import.meta.url) const { assertNodePtyJobOwnership, nodePtyAddonPath } = require('./node-pty-job-ownership.cjs') @@ -420,7 +421,7 @@ function runNodeGyp({ args, cwd }) { const env = process.platform === 'linux' ? { ...process.env, CXXFLAGS: `${process.env.CXXFLAGS ?? ''} -std=gnu++2a`.trim() } - : process.env + : disableMsbuildFileTrackingOnWindows({ ...process.env }) const result = runProcessSync({ program: process.execPath, args, diff --git a/config/scripts/ensure-native-runtime.test.mjs b/config/scripts/ensure-native-runtime.test.mjs index 4d6e022b185..3cd01374d1f 100644 --- a/config/scripts/ensure-native-runtime.test.mjs +++ b/config/scripts/ensure-native-runtime.test.mjs @@ -60,6 +60,7 @@ describe('ensure-native-runtime', () => { expect(result.stderr).toContain('node-gyp stderr complete\n') const log = readFileSync(logPath, 'utf8') expect(log).toContain(`node-gyp rebuild --arch=${process.arch}\n`) + expect(log).toContain(`trackFileAccess=${process.platform === 'win32' ? 'false' : ''}\n`) expect(log).toContain(join('node_modules', 'node-pty')) if (process.platform === 'linux') { expect(log).toMatch(/^cxxflags=(?:.*\s)?-std=gnu\+\+2a$/m) @@ -73,9 +74,14 @@ describe('ensure-native-runtime', () => { } }) - it.skipIf(process.platform !== 'win32')( - 'rebuilds other failed Windows addons with patched node-pty', - () => { + it.skipIf(process.platform !== 'win32').each([ + { trackingEnv: {}, tracking: 'false' }, + { trackingEnv: { TrackFileAccess: 'true' }, tracking: 'true' }, + { trackingEnv: { trackfileaccess: 'true' }, tracking: 'true' }, + { trackingEnv: { tRaCkFiLeAcCeSs: 'false' }, tracking: 'false' } + ])( + 'rebuilds other failed Windows addons with patched node-pty and tracking=$tracking', + ({ trackingEnv, tracking }) => { const projectDir = mkTempProject() try { @@ -90,6 +96,7 @@ describe('ensure-native-runtime', () => { cwd: projectDir, encoding: 'utf8', env: envForNativeFixture(projectDir, { + ...trackingEnv, ORCA_NATIVE_TEST_LOG: logPath, ORCA_NATIVE_TEST_MARKER: markerPath }) @@ -100,6 +107,9 @@ describe('ensure-native-runtime', () => { expect( log.split('\n').filter((line) => line === `node-gyp rebuild --arch=${process.arch}`) ).toHaveLength(2) + expect( + log.split('\n').filter((line) => line === `trackFileAccess=${tracking}`) + ).toHaveLength(2) expect(log).toContain(join('node_modules', 'node-pty')) expect(log).toContain(join('node_modules', '@orca', 'windows-registry')) } finally { @@ -277,8 +287,12 @@ function mkTempProject() { } function envForNativeFixture(projectDir, extraEnv) { + // An inherited tracking preference would mask the Windows default under test. + const inherited = Object.fromEntries( + Object.entries(process.env).filter(([key]) => key.toLowerCase() !== 'trackfileaccess') + ) return { - ...process.env, + ...inherited, ...extraEnv, npm_config_node_gyp: join(projectDir, 'node_modules', 'node-gyp', 'bin', 'node-gyp.js') } @@ -323,7 +337,7 @@ exports.loadNativeModule = function loadNativeModule(nativeName) { writeFakeWindowsRegistry(projectDir, { requiresMarker: windowsRegistryRequiresMarker }) if (process.platform === 'win32') { const buildDir = join(nodePtyDir, 'build', 'Release') - mkdirSync(buildDir, { recursive: true }) + writePatchedNodePtyBuildArtifacts(projectDir) writeFileSync(join(buildDir, 'conpty.node'), Buffer.from('msys-2.0.dll', 'utf16le')) } } @@ -419,6 +433,7 @@ const { appendFileSync, writeFileSync, writeSync } = require('node:fs') appendFileSync(process.env.ORCA_NATIVE_TEST_LOG, \`node-gyp \${process.argv.slice(2).join(' ')}\\n\`) appendFileSync(process.env.ORCA_NATIVE_TEST_LOG, \`cwd=\${process.cwd()}\\n\`) appendFileSync(process.env.ORCA_NATIVE_TEST_LOG, \`cxxflags=\${process.env.CXXFLAGS || ''}\\n\`) +appendFileSync(process.env.ORCA_NATIVE_TEST_LOG, \`trackFileAccess=\${process.env.TrackFileAccess ?? ''}\\n\`) if (process.env.ORCA_NATIVE_TEST_VERBOSE_OUTPUT_BYTES) { const output = Buffer.alloc(Number(process.env.ORCA_NATIVE_TEST_VERBOSE_OUTPUT_BYTES), 'x') for (let offset = 0; offset < output.length;) { diff --git a/config/scripts/msbuild-file-tracking.mjs b/config/scripts/msbuild-file-tracking.mjs new file mode 100644 index 00000000000..0a2fac8b804 --- /dev/null +++ b/config/scripts/msbuild-file-tracking.mjs @@ -0,0 +1,14 @@ +// FileTracker's long-path-unsafe .tlog files serve incremental builds; these rebuilds are forced. +export function disableMsbuildFileTrackingOnWindows( + env = process.env, + platform = process.platform +) { + // Windows environment keys are case-insensitive, including caller overrides in copied objects. + if ( + platform === 'win32' && + !Object.keys(env).some((key) => key.toLowerCase() === 'trackfileaccess') + ) { + env.TrackFileAccess = 'false' + } + return env +} diff --git a/config/scripts/msbuild-file-tracking.test.mjs b/config/scripts/msbuild-file-tracking.test.mjs new file mode 100644 index 00000000000..fdde02db25c --- /dev/null +++ b/config/scripts/msbuild-file-tracking.test.mjs @@ -0,0 +1,28 @@ +import { describe, expect, it } from 'vitest' +import { disableMsbuildFileTrackingOnWindows } from './msbuild-file-tracking.mjs' + +describe('disableMsbuildFileTrackingOnWindows', () => { + it('turns tracking off on Windows when the caller left it unset', () => { + expect(disableMsbuildFileTrackingOnWindows({ PATH: 'x' }, 'win32')).toEqual({ + PATH: 'x', + TrackFileAccess: 'false' + }) + }) + + it.each(['TrackFileAccess', 'trackfileaccess', 'TRACKFILEACCESS', 'tRaCkFiLeAcCeSs'])( + 'preserves explicit %s values without adding a duplicate key', + (key) => { + for (const value of ['true', 'false', '']) { + const env = { [key]: value } + expect(disableMsbuildFileTrackingOnWindows(env, 'win32')).toBe(env) + expect(env).toEqual({ [key]: value }) + } + } + ) + + it.each(['linux', 'darwin'])('leaves %s hosts alone', (platform) => { + const env = { PATH: 'x' } + expect(disableMsbuildFileTrackingOnWindows(env, platform)).toBe(env) + expect(env).toEqual({ PATH: 'x' }) + }) +}) diff --git a/config/scripts/rebuild-native-deps-node-pty.test.mjs b/config/scripts/rebuild-native-deps-node-pty.test.mjs index a9ec246dd95..d1153c5f20f 100644 --- a/config/scripts/rebuild-native-deps-node-pty.test.mjs +++ b/config/scripts/rebuild-native-deps-node-pty.test.mjs @@ -22,6 +22,51 @@ import { } from './rebuild-native-deps-test-fixtures.mjs' describe('rebuild-native-deps patched node-pty rebuild', () => { + it.skipIf(process.platform !== 'win32')( + 'passes the Windows tracking default and explicit overrides to forced Electron rebuilds', + () => { + const projectDir = mkTempProject() + + try { + const rebuildLogPath = join(projectDir, 'electron-rebuild.log') + writeFakeUsableElectronPackage(projectDir, { platform: 'win32' }) + writeFakeElectronRebuild(projectDir, { logPathEnv: 'ORCA_REBUILD_TEST_LOG' }) + writeFakeLoadableNodePty(projectDir) + writeFakeWindowsProcessTree(projectDir) + writeFakeNodePtyConptyPayload(projectDir, process.arch) + + const env = { + ORCA_REBUILD_TEST_LOG: rebuildLogPath, + npm_config_platform: 'win32', + npm_config_arch: process.arch + } + for (const override of [ + {}, + { TrackFileAccess: 'true' }, + { trackfileaccess: 'true' }, + { tRaCkFiLeAcCeSs: 'false' } + ]) { + const result = runRebuildScript(projectDir, { ...env, ...override }) + expect(result.status, result.stderr).toBe(0) + } + + const calls = readFileSync(rebuildLogPath, 'utf8') + .trim() + .split('\n') + .map((line) => JSON.parse(line)) + expect(calls.map((call) => call.trackFileAccess)).toEqual([ + 'false', + 'true', + 'true', + 'false' + ]) + expect(calls.every((call) => call.force)).toBe(true) + } finally { + removeTreeSync(projectDir) + } + } + ) + it.skipIf(process.platform !== 'win32')( 'repairs a missing ConPTY runtime before probing without recompiling node-pty', () => { diff --git a/config/scripts/rebuild-native-deps-test-fixtures.mjs b/config/scripts/rebuild-native-deps-test-fixtures.mjs index 333c8f939ef..83223b63042 100644 --- a/config/scripts/rebuild-native-deps-test-fixtures.mjs +++ b/config/scripts/rebuild-native-deps-test-fixtures.mjs @@ -110,7 +110,7 @@ export function writeWindowsProcessTreePatchFile(projectDir) { export function mkTempProject() { const projectDir = mkdtempSync(join(tmpdir(), 'orca-rebuild-native-deps-')) mkdirSync(join(projectDir, 'config', 'scripts'), { recursive: true }) - copyFileSync(sourceScriptPath, join(projectDir, 'config', 'scripts', 'rebuild-native-deps.mjs')) + copyScriptWithLocalModules(sourceScriptPath, join(projectDir, 'config', 'scripts')) copyScriptWithLocalModules(sourceInstallScriptPath, join(projectDir, 'config', 'scripts')) copyScriptWithLocalModules(sourceNodePtyJobOwnershipPath, join(projectDir, 'config', 'scripts')) copyFileSync( @@ -134,7 +134,8 @@ export function runRebuildScript(projectDir, extraEnv = {}, args = []) { for (const key of Object.keys(env)) { if ( key.toLowerCase() === 'orca_strict_electron_install' || - key.toLowerCase() === 'npm_lifecycle_event' + key.toLowerCase() === 'npm_lifecycle_event' || + key.toLowerCase() === 'trackfileaccess' ) { delete env[key] } @@ -286,6 +287,7 @@ export async function rebuild(options) {${emitAddon} electronVersion: options.electronVersion, force: options.force, ignoreModules: options.ignoreModules, + trackFileAccess: process.env.TrackFileAccess ?? null, onlyModules: options.onlyModules, platform: options.platform }) + '\\n' diff --git a/config/scripts/rebuild-native-deps.mjs b/config/scripts/rebuild-native-deps.mjs index 68de5d49e21..e565eeca97e 100644 --- a/config/scripts/rebuild-native-deps.mjs +++ b/config/scripts/rebuild-native-deps.mjs @@ -26,6 +26,7 @@ import { stageWindowsProcessTreeNodeAddonApiHeaders, windowsProcessTreeAddonPath } from './windows-process-tree-gyp-rebuild.mjs' +import { disableMsbuildFileTrackingOnWindows } from './msbuild-file-tracking.mjs' import { copyFileSync, existsSync, @@ -162,6 +163,7 @@ try { console.warn('[rebuild] Repaired the un-applied windows-process-tree command-line patch.') } } + disableMsbuildFileTrackingOnWindows() await rebuild({ buildPath: projectDir, electronVersion,