windows-development-sandbox 1.18.4 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/config.example.json +9 -0
- package/dist/app/features/accounts/accounts.def.d.ts +4 -0
- package/dist/app/features/accounts/accounts.run.d.ts +1 -0
- package/dist/app/features/accounts/services.def.d.ts +3 -0
- package/dist/app/features/accounts/services.def.js +4 -2
- package/dist/app/features/accounts/services.run.d.ts +1 -0
- package/dist/app/features/accounts/services.run.js +3 -1
- package/dist/app/features/agent-commands/agent-commands.def.d.ts +37 -0
- package/dist/app/features/agent-commands/agent-commands.def.js +91 -0
- package/dist/app/features/agent-commands/agent-commands.run.d.ts +28 -0
- package/dist/app/features/agent-commands/agent-commands.run.js +35 -0
- package/dist/app/features/agent-commands/index.d.ts +2 -0
- package/dist/app/features/agent-commands/index.js +3 -0
- package/dist/app/features/agents/agents.def.d.ts +14 -1
- package/dist/app/features/agents/agents.def.js +61 -6
- package/dist/app/features/agents/agents.run.d.ts +1 -0
- package/dist/app/features/agents/agents.run.js +8 -1
- package/dist/app/features/agents/claude-env.def.d.ts +36 -0
- package/dist/app/features/agents/claude-env.def.js +305 -0
- package/dist/app/features/agents/env.def.d.ts +163 -0
- package/dist/app/features/agents/env.def.js +184 -0
- package/dist/app/features/agents/env.run.d.ts +43 -0
- package/dist/app/features/agents/env.run.js +86 -0
- package/dist/app/features/agents/index.d.ts +2 -0
- package/dist/app/features/agents/index.js +2 -0
- package/dist/app/features/defs.d.ts +132 -1
- package/dist/app/features/defs.js +9 -0
- package/dist/app/features/settings/categories.def.js +5 -2
- package/dist/app/features/settings/settings-rows.js +5 -1
- package/dist/app/features/settings/settings.run.d.ts +8 -0
- package/dist/app/features/settings/settings.run.js +9 -0
- package/dist/app/features/updates/updates.run.js +12 -2
- package/dist/app/features/vpn/vpn.def.d.ts +33 -0
- package/dist/app/features/vpn/vpn.def.js +30 -6
- package/dist/app/features/vpn/vpn.run.d.ts +11 -0
- package/dist/app/features/vpn/vpn.run.js +4 -1
- package/dist/app/kernel/rpc.d.ts +1 -1
- package/dist/app/registry.js +7 -1
- package/dist/bin/agent-sandbox.js +2 -2
- package/dist/bin/{chunk-MY7NLQQY.js → chunk-2IMT7ZCT.js} +1 -1
- package/dist/bin/{chunk-N4GXYTLV.js → chunk-DK4NTBUF.js} +69 -26
- package/dist/bin/{chunk-FRPLQLSY.js → chunk-Z2P5OGKL.js} +4323 -3463
- package/dist/bin/claude-host.js +2 -2
- package/dist/bin/claude-sbx.js +2 -2
- package/dist/bin/codex-sbx.js +2 -2
- package/dist/bin/gemini-sbx.js +2 -2
- package/dist/bin/sandbox.js +2 -2
- package/dist/bin/sbx-hide.js +2 -2
- package/dist/bin/sbx-show.js +2 -2
- package/dist/cli/commands/agent-commands.d.ts +0 -1
- package/dist/cli/commands/agent-commands.js +2 -32
- package/dist/cli/commands/index.d.ts +1 -1
- package/dist/cli/commands/index.js +1 -1
- package/dist/cli/commands/launch/launch.js +2 -2
- package/dist/cli/commands/registry.js +3 -2
- package/dist/cli/manifest.d.ts +1 -1
- package/dist/cli/manifest.js +1 -1
- package/dist/cli/menu/menu.d.ts +1 -1
- package/dist/cli/menu/menu.js +3 -3
- package/dist/cli/menu/pages/agents-page.d.ts +7 -2
- package/dist/cli/menu/pages/agents-page.js +67 -11
- package/dist/cli/menu/pages/maintenance-page.js +1 -2
- package/dist/cli/menu/pages/settings-page.js +1 -0
- package/dist/cli/menu/views.js +2 -1
- package/dist/cli/services/agent-commands-service.d.ts +2 -0
- package/dist/cli/services/agent-commands-service.js +13 -7
- package/dist/cli/services/vpn-service.d.ts +5 -1
- package/dist/cli/services/vpn-service.js +6 -1
- package/dist/cli/tui/dialogs/text-screen.js +2 -1
- package/dist/cli/tui/terminal.js +3 -1
- package/dist/core/config/defaults.js +9 -0
- package/dist/core/detect/toolchains.js +9 -0
- package/dist/core/disk.d.ts +1 -5
- package/dist/core/disk.js +1 -16
- package/dist/core/schema/codex.d.ts +29 -0
- package/dist/core/schema/codex.js +24 -0
- package/dist/core/schema/config.d.ts +46 -14
- package/dist/core/schema/config.js +2 -0
- package/dist/core/schema/index.d.ts +1 -0
- package/dist/core/schema/index.js +1 -0
- package/dist/core/schema/toolchains.d.ts +4 -0
- package/dist/core/schema/toolchains.js +3 -0
- package/dist/core/types.d.ts +2 -0
- package/dist/guest/accounts/apply.d.ts +6 -6
- package/dist/guest/agents/agent-files.d.ts +26 -6
- package/dist/guest/agents/agent-files.js +84 -14
- package/dist/guest/agents/local-state.d.ts +5 -2
- package/dist/guest/agents/local-state.js +24 -10
- package/dist/guest/boot/boot.js +10 -5
- package/dist/guest/daemon/sessions.js +2 -1
- package/dist/guest/toolchains/installers.d.ts +6 -10
- package/dist/guest/toolchains/installers.js +4 -12
- package/dist/guest/toolchains/native/autosdk.d.ts +3 -0
- package/dist/guest/toolchains/native/autosdk.js +50 -0
- package/dist/guest/toolchains/native/unreal.js +3 -0
- package/dist/guest/toolchains/native/vcruntime.d.ts +0 -3
- package/dist/guest/toolchains/native/vcruntime.js +2 -5
- package/dist/guest/toolchains/native/visual-studio.d.ts +2 -2
- package/dist/guest/toolchains/native/visual-studio.js +100 -16
- package/dist/guest/toolchains/native/vs-request.d.ts +21 -0
- package/dist/guest/toolchains/native/vs-request.js +60 -1
- package/dist/guest/toolchains/not-yet.d.ts +1 -1
- package/dist/guest/toolchains/not-yet.js +3 -3
- package/dist/guest/toolchains/persisted.js +2 -7
- package/dist/guest/toolchains/plan.js +9 -4
- package/dist/guest/toolchains/signature.d.ts +5 -0
- package/dist/guest/toolchains/signature.js +22 -0
- package/dist/guest/vpn/tunnel.d.ts +3 -0
- package/dist/guest/vpn/tunnel.js +10 -12
- package/dist/guest/vpn/vpn.js +6 -1
- package/dist/host/accounts/leases.d.ts +3 -3
- package/dist/host/accounts/source.js +1 -1
- package/dist/host/agent-commands/commands.js +1 -1
- package/dist/host/agent-commands/forwarders.d.ts +14 -2
- package/dist/host/agent-commands/forwarders.js +31 -13
- package/dist/host/agent-commands/reconcile.d.ts +2 -1
- package/dist/host/agent-commands/reconcile.js +7 -8
- package/dist/host/agent-commands/user-path.d.ts +5 -0
- package/dist/host/agent-commands/user-path.js +13 -0
- package/dist/host/launch/session-file.js +1 -0
- package/dist/host/profile/accounts.js +2 -4
- package/dist/host/profile/claude-login.d.ts +3 -2
- package/dist/host/profile/claude-login.js +3 -13
- package/dist/host/vpn/index.d.ts +2 -1
- package/dist/host/vpn/index.js +2 -1
- package/dist/host/vpn/last.d.ts +28 -0
- package/dist/host/vpn/last.js +43 -0
- package/dist/host/vpn/login.d.ts +3 -1
- package/dist/host/vpn/login.js +17 -3
- package/dist/runtime/agent.js +28 -0
- package/dist/runtime/daemon.js +1117 -912
- package/dist/runtime/db.js +30 -0
- package/dist/runtime/main.js +1308 -1036
- package/dist/runtime/netproxy.js +22 -0
- package/dist/runtime/tool.js +238 -208
- package/dist/runtime/view.js +22 -0
- package/dist/selftest/agent-commands.test.js +45 -7
- package/dist/selftest/agent-paths.test.js +1 -12
- package/dist/selftest/agent-settings.test.d.ts +2 -0
- package/dist/selftest/agent-settings.test.js +94 -0
- package/dist/selftest/app.test.js +2 -1
- package/dist/selftest/cli.test.js +4 -0
- package/dist/selftest/index.js +2 -0
- package/dist/selftest/menu.test.js +5 -3
- package/dist/selftest/pc-installs.test.js +5 -0
- package/dist/selftest/persisted-installs.test.js +69 -0
- package/dist/selftest/service-accounts.test.js +1 -1
- package/dist/selftest/support/engine-golden.js +1 -1
- package/dist/selftest/support/fixtures.d.ts +9 -0
- package/dist/selftest/vpn.test.js +51 -3
- package/dist/shared/accounts/anthropic.d.ts +5 -0
- package/dist/shared/accounts/anthropic.js +15 -0
- package/dist/shared/accounts/index.d.ts +1 -0
- package/dist/shared/accounts/index.js +1 -0
- package/dist/shared/channel/schema.d.ts +19 -0
- package/dist/shared/channel/schema.js +2 -0
- package/dist/shared/io/disk.d.ts +9 -0
- package/dist/shared/io/disk.js +19 -0
- package/dist/shared/io/index.d.ts +1 -0
- package/dist/shared/io/index.js +1 -0
- package/dist/shared/readiness.d.ts +9 -0
- package/dist/shared/readiness.js +11 -0
- package/dist/shared/signer.d.ts +24 -0
- package/dist/shared/signer.js +15 -0
- package/dist/shared/vpn/failure.d.ts +10 -0
- package/dist/shared/vpn/failure.js +28 -0
- package/dist/sync/watcher.js +2 -0
- package/dist/web/index.html +1 -1
- package/dist/web/{main-GVZWWP4I.js → main-OUUQYNK4.js} +75 -74
- package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
- package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
- package/native/csharp/HostTools/Program.cs +14 -1
- package/native/csharp/HostTools/UserPath.cs +17 -0
- package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
- package/native/scripts/edit-user-path.ps1 +6 -2
- package/package.json +1 -1
- package/sandbox/scripts/get-signature.ps1 +13 -0
- package/schemas/config.schema.json +64 -0
- package/schemas/sandbox-config.schema.json +8 -0
- package/sandbox/scripts/get-signer.ps1 +0 -8
|
@@ -10,7 +10,7 @@ import { execOutput } from '../runtime/process.js';
|
|
|
10
10
|
import { log, warn } from '../runtime/log.js';
|
|
11
11
|
import { session, stateDir } from '../runtime/session.js';
|
|
12
12
|
import { errMsg } from '../../core/util/errors.js';
|
|
13
|
-
import { bootResetPaths,
|
|
13
|
+
import { bootResetPaths, codexConfigOwned, MAX_CODEX_CONFIG_BYTES, isCopyableDatabaseFile, isDatabaseFile, localStateEntries, } from './agent-files.js';
|
|
14
14
|
const SQLITE_HEADER = 'SQLite format 3\0';
|
|
15
15
|
/** The sandbox-disk folder that holds the working copies. */
|
|
16
16
|
const localRoot = () => path.join(localDir, 'agent-state');
|
|
@@ -102,17 +102,31 @@ export function resetMachineBound() {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
-
/**
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Boot: Codex's config.toml follows the codex settings (codexConfigOwned): no Windows sandbox setup of its own, its MCP
|
|
107
|
+
* logins in a file. The sandbox mode itself travels as launch flags (codexArguments), which beat this file.
|
|
108
|
+
*/
|
|
109
|
+
export function codexBootConfig() {
|
|
110
|
+
if (dryRun || !session.agents?.codex || !session.codex)
|
|
108
111
|
return;
|
|
109
112
|
const file = path.join(stateDir, 'codex', 'config.toml');
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
try {
|
|
114
|
+
const size = exists(file) ? fs.statSync(file).size : 0;
|
|
115
|
+
if (size > MAX_CODEX_CONFIG_BYTES) {
|
|
116
|
+
warn('codex: config.toml is over 1 MB; left as it is.');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const before = size ? fs.readFileSync(file, 'utf8') : '';
|
|
120
|
+
const after = codexConfigOwned(before, session.codex);
|
|
121
|
+
if (after === before)
|
|
122
|
+
return;
|
|
123
|
+
mkdirp(path.dirname(file));
|
|
124
|
+
fs.writeFileSync(file, after, 'utf8');
|
|
125
|
+
log('codex: config.toml updated (Windows sandbox "' + session.codex.windowsSandbox + '").');
|
|
126
|
+
}
|
|
127
|
+
catch (e) {
|
|
128
|
+
warn('codex: could not update config.toml (' + errMsg(e) + ').');
|
|
129
|
+
}
|
|
116
130
|
}
|
|
117
131
|
/** Boot: the saved databases of every configured agent are copied to the sandbox's disk, where the agent runs them. */
|
|
118
132
|
export function restoreLocalState() {
|
package/dist/guest/boot/boot.js
CHANGED
|
@@ -5,8 +5,8 @@ import { spawn } from 'node:child_process';
|
|
|
5
5
|
import { writeGuides } from './guide.js';
|
|
6
6
|
import { setupToolchains } from '../toolchains/index.js';
|
|
7
7
|
import { agents } from '../agents/agent-installer.js';
|
|
8
|
-
import { agentShims, sbxAgentText, shimText } from '../agents/agent-files.js';
|
|
9
|
-
import {
|
|
8
|
+
import { agentArguments, agentShims, codexArguments, sbxAgentText, shimText } from '../agents/agent-files.js';
|
|
9
|
+
import { codexBootConfig, resetMachineBound, restoreLocalState } from '../agents/local-state.js';
|
|
10
10
|
import { installShellPath } from '../setup/shell-path.js';
|
|
11
11
|
import { linkProfileDirs } from '../agents/profile-links.js';
|
|
12
12
|
import { restoreClaudeMem } from '../setup/claude-mem.js';
|
|
@@ -120,7 +120,7 @@ function writeDevToolCommands(dir) {
|
|
|
120
120
|
*/
|
|
121
121
|
function writeAgentCommands(dir) {
|
|
122
122
|
const install = { node: process.execPath, script: path.join(path.dirname(process.argv[1]), 'agent.js') };
|
|
123
|
-
for (const shim of agentShims(toolsDir, session.agents ?? {}))
|
|
123
|
+
for (const shim of agentShims(toolsDir, session.agents ?? {}, session.codex ? { codex: codexArguments(session.codex) } : {}))
|
|
124
124
|
fs.writeFileSync(path.join(dir, shim.command + '.cmd'), shimText(shim, install));
|
|
125
125
|
fs.writeFileSync(path.join(dir, 'sbx-agent.cmd'), sbxAgentText(install));
|
|
126
126
|
}
|
|
@@ -280,7 +280,7 @@ export class Boot {
|
|
|
280
280
|
await soft('Claude settings', () => syncClaudeProfile(this.secrets.claudeCredentials));
|
|
281
281
|
await soft('claude-mem restore', restoreClaudeMem);
|
|
282
282
|
await soft('Agent files of the last sandbox', resetMachineBound);
|
|
283
|
-
await soft('Codex
|
|
283
|
+
await soft('Codex settings', codexBootConfig);
|
|
284
284
|
await soft('Agent databases restore', restoreLocalState);
|
|
285
285
|
// Agent add-ons: MCP servers in the sandbox's Claude Code, and the Headroom proxy before the managed settings point
|
|
286
286
|
// Claude Code at it (the guide reads them back, so it comes last).
|
|
@@ -316,7 +316,12 @@ export class Boot {
|
|
|
316
316
|
if (dryRun) {
|
|
317
317
|
const launch = agents.resolveLaunch(session.harnessName, session.harness);
|
|
318
318
|
log('(dry run) would run: ' +
|
|
319
|
-
[
|
|
319
|
+
[
|
|
320
|
+
launch.file,
|
|
321
|
+
...launch.args,
|
|
322
|
+
...agentArguments(session.harnessName, session.harness, session.codex),
|
|
323
|
+
...session.harnessArguments,
|
|
324
|
+
].join(' ') +
|
|
320
325
|
' | cwd=' +
|
|
321
326
|
workspace);
|
|
322
327
|
runHooks('post-agent', env);
|
|
@@ -18,6 +18,7 @@ import { freePort } from '../runtime/ports.js';
|
|
|
18
18
|
import { primaryProject } from '../boot/project-status.js';
|
|
19
19
|
import { endLeftoverTree, ensurePtyHost, ptyHostLogTail, recordedTrees, startPtyHost, waitForPtyHost, } from '../sessions/pty-host.js';
|
|
20
20
|
import { agents } from '../agents/agent-installer.js';
|
|
21
|
+
import { agentArguments } from '../agents/agent-files.js';
|
|
21
22
|
import { saveClaudeMem } from '../setup/claude-mem.js';
|
|
22
23
|
import { saveLocalState } from '../agents/local-state.js';
|
|
23
24
|
import { projectHooks, runHooks, runHooksAsync } from '../setup/hooks.js';
|
|
@@ -108,7 +109,7 @@ const agentProgram = async (req, env, say) => {
|
|
|
108
109
|
say('Preparing ' + req.harness);
|
|
109
110
|
await agents.provision(req.harness, harness, env, false);
|
|
110
111
|
const launch = agents.resolveLaunch(req.harness, harness);
|
|
111
|
-
const base = [...launch.args, ...harness.
|
|
112
|
+
const base = [...launch.args, ...agentArguments(req.harness, harness, session.codex)];
|
|
112
113
|
return {
|
|
113
114
|
command: commandLine(launch.file, [...base, ...req.args]),
|
|
114
115
|
restartCommand: commandLine(launch.file, [...base, ...(harness.resumeArguments ?? [])]),
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
readonly unity: RegExp;
|
|
6
|
-
};
|
|
7
|
-
/** Whether a file's signature is the expected signer's. */
|
|
8
|
-
export declare function signedBy(file: string, signer: string | RegExp): boolean;
|
|
1
|
+
import { SIGNERS, type AppSigner } from '../../shared/signer.js';
|
|
2
|
+
import { signedBy } from './signature.js';
|
|
3
|
+
export { SIGNERS };
|
|
4
|
+
export { signedBy };
|
|
9
5
|
/** A downloader `verify` hook: refuses a file not signed by `signer` (it counts as integrity for requireChecksum). */
|
|
10
|
-
export declare function signatureCheck(signer:
|
|
6
|
+
export declare function signatureCheck(signer: AppSigner, what: string): (file: string) => void;
|
|
11
7
|
/** The installer at `file` (downloaded once from `url`), checked against its signer and, when given, its SHA-256. */
|
|
12
|
-
export declare function signedInstaller(url: string, file: string, what: string, signer:
|
|
8
|
+
export declare function signedInstaller(url: string, file: string, what: string, signer: AppSigner, sha256?: string | null): Promise<string>;
|
|
@@ -5,18 +5,10 @@ import path from 'node:path';
|
|
|
5
5
|
import { downloader } from '../../shared/download/index.js';
|
|
6
6
|
import { exists, mkdirp } from '../runtime/fs.js';
|
|
7
7
|
import { log } from '../runtime/log.js';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
valve: /^CN="?Valve(,| Corp)/,
|
|
13
|
-
unity: /^CN="?Unity Technologies/,
|
|
14
|
-
};
|
|
15
|
-
/** Whether a file's signature is the expected signer's. */
|
|
16
|
-
export function signedBy(file, signer) {
|
|
17
|
-
const subject = signerOf(file);
|
|
18
|
-
return typeof signer === 'string' ? subject === signer : signer.test(subject);
|
|
19
|
-
}
|
|
8
|
+
import { SIGNERS } from '../../shared/signer.js';
|
|
9
|
+
import { signedBy } from './signature.js';
|
|
10
|
+
export { SIGNERS };
|
|
11
|
+
export { signedBy };
|
|
20
12
|
/** A downloader `verify` hook: refuses a file not signed by `signer` (it counts as integrity for requireChecksum). */
|
|
21
13
|
export function signatureCheck(signer, what) {
|
|
22
14
|
return (file) => {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Unreal without Visual Studio (toolchains.unreal.visualStudio = "autosdk", experimental): an AutoSDK tree made from
|
|
2
|
+
// the portable MSVC (tools\msvc), at tools\unreal\autosdk, which UnrealBuildTool finds through UE_SDKS_ROOT. Made of
|
|
3
|
+
// copies (mapped folders refuse links), once; kept between sandboxes. The editor's Visual Studio integration is not
|
|
4
|
+
// available.
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { writeJsonAtomic } from '../../../shared/io/json.js';
|
|
9
|
+
import { robocopyFailed } from '../../../shared/io/robocopy-result.js';
|
|
10
|
+
import { spawnAsync } from '../../../shared/download/index.js';
|
|
11
|
+
import { SYSTEM } from '../../../shared/system.js';
|
|
12
|
+
import { exists, mkdirp } from '../../runtime/fs.js';
|
|
13
|
+
import { log } from '../../runtime/log.js';
|
|
14
|
+
import { tool } from '../tool-env.js';
|
|
15
|
+
import { hasCompiler, readInstalled, ROOT as MSVC_ROOT } from './msvc/tree.js';
|
|
16
|
+
import { autosdkLayout } from './vs-request.js';
|
|
17
|
+
const AUTOSDK_DIR = tool('unreal', 'autosdk');
|
|
18
|
+
const MARKER = path.join(AUTOSDK_DIR, '.complete.json');
|
|
19
|
+
const MarkerSchema = z.object({ msvc: z.string(), sdk: z.string() });
|
|
20
|
+
function builtFor(msvc, sdk) {
|
|
21
|
+
try {
|
|
22
|
+
const done = MarkerSchema.parse(JSON.parse(fs.readFileSync(MARKER, 'utf8')));
|
|
23
|
+
return done.msvc === msvc && done.sdk === sdk;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false; // No marker, or a half-written one: build again.
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** Makes the AutoSDK tree from the portable MSVC if it is missing for this MSVC and SDK, and points Unreal at it. */
|
|
30
|
+
export async function setupAutosdk(env) {
|
|
31
|
+
const msvc = readInstalled();
|
|
32
|
+
if (!msvc || !hasCompiler(msvc.msvc))
|
|
33
|
+
throw new Error('toolchains.unreal.visualStudio = "autosdk" builds from the portable MSVC, which is not installed ' +
|
|
34
|
+
'(is toolchains.msvc off?)');
|
|
35
|
+
if (!builtFor(msvc.msvc, msvc.sdk)) {
|
|
36
|
+
log('Making the Unreal AutoSDK from MSVC ' + msvc.msvc + ' (~2 GB, once) ...');
|
|
37
|
+
fs.rmSync(MARKER, { force: true });
|
|
38
|
+
for (const pair of autosdkLayout(MSVC_ROOT, msvc.msvc, msvc.sdk, AUTOSDK_DIR)) {
|
|
39
|
+
mkdirp(pair.to);
|
|
40
|
+
const code = await spawnAsync(SYSTEM.robocopy(), [pair.from, pair.to, '/E', '/COPY:DAT', '/R:1', '/W:1', '/NP']);
|
|
41
|
+
if (robocopyFailed(code))
|
|
42
|
+
throw new Error('could not copy ' + pair.from + ' into the AutoSDK (robocopy ' + code + ')');
|
|
43
|
+
}
|
|
44
|
+
writeJsonAtomic(MARKER, { msvc: msvc.msvc, sdk: msvc.sdk });
|
|
45
|
+
}
|
|
46
|
+
if (!exists(MARKER))
|
|
47
|
+
throw new Error('the Unreal AutoSDK was not completed');
|
|
48
|
+
env.vars['UE_SDKS_ROOT'] = AUTOSDK_DIR;
|
|
49
|
+
env.summary.push('unreal autosdk: MSVC ' + msvc.msvc + ', Windows SDK ' + msvc.sdk + ' (UE_SDKS_ROOT=' + AUTOSDK_DIR + ')');
|
|
50
|
+
}
|
|
@@ -12,6 +12,7 @@ import { log } from '../../runtime/log.js';
|
|
|
12
12
|
import { setupUnrealEngines } from '../engines/unreal-engines.js';
|
|
13
13
|
import { SIGNERS, signedInstaller } from '../installers.js';
|
|
14
14
|
import { tool } from '../tool-env.js';
|
|
15
|
+
import { setupAutosdk } from './autosdk.js';
|
|
15
16
|
import { installedVs } from './visual-studio.js';
|
|
16
17
|
export { UNREAL_COMPONENTS } from './vs-request.js';
|
|
17
18
|
const DIRECTX_URL = 'https://download.microsoft.com/download/8/4/A/84A35BF1-DAFE-4AE8-82AF-AD2AE20B6B14/directx_Jun2010_redist.exe';
|
|
@@ -39,6 +40,8 @@ export async function setupUnreal(env, config, ctx) {
|
|
|
39
40
|
return;
|
|
40
41
|
}
|
|
41
42
|
await installDirectX();
|
|
43
|
+
if (config.visualStudio === 'autosdk')
|
|
44
|
+
await setupAutosdk(env);
|
|
42
45
|
await setupUnrealEngines(env, config, ctx);
|
|
43
46
|
const vs = installedVs();
|
|
44
47
|
env.summary.push('unreal: ' +
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { type ToolEnv } from '../tool-env.js';
|
|
2
|
-
export declare const MICROSOFT_SIGNER = "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US";
|
|
3
|
-
/** The subject of a valid Authenticode signature, or '' when the file is unsigned or tampered with. */
|
|
4
|
-
export declare const signerOf: (file: string) => string;
|
|
5
2
|
/**
|
|
6
3
|
* A real installer (it writes to System32), so it runs on every boot; the installer itself is kept in persistent
|
|
7
4
|
* storage and only run when Microsoft's signature on it checks out. Resolves true when this call ran the installer.
|
|
@@ -3,17 +3,14 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { spawnAsync } from '../../../shared/download/index.js';
|
|
5
5
|
import { dryRun } from '../../runtime/paths.js';
|
|
6
|
-
import { scriptOutput } from '../../runtime/process.js';
|
|
7
6
|
import { exists } from '../../runtime/fs.js';
|
|
8
7
|
import { log } from '../../runtime/log.js';
|
|
9
8
|
import { downloadFile } from '../install.js';
|
|
9
|
+
import { SIGNERS, signedBy } from '../installers.js';
|
|
10
10
|
import { tool } from '../tool-env.js';
|
|
11
11
|
const VC_REDIST = 'https://aka.ms/vs/17/release/vc_redist.x64.exe';
|
|
12
|
-
export const MICROSOFT_SIGNER = 'CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US';
|
|
13
12
|
/** 1638: a newer version is already installed; 3010: installed, a restart would finish it (not needed for the DLLs). */
|
|
14
13
|
const OK_EXIT_CODES = [0, 1638, 3010];
|
|
15
|
-
/** The subject of a valid Authenticode signature, or '' when the file is unsigned or tampered with. */
|
|
16
|
-
export const signerOf = (file) => scriptOutput('get-signer.ps1', ['-Path', file]).trim();
|
|
17
14
|
/**
|
|
18
15
|
* A real installer (it writes to System32), so it runs on every boot; the installer itself is kept in persistent
|
|
19
16
|
* storage and only run when Microsoft's signature on it checks out. Resolves true when this call ran the installer.
|
|
@@ -32,7 +29,7 @@ export async function setupVcRuntime(env, summary = true) {
|
|
|
32
29
|
log('Downloading the Visual C++ runtime (~25 MB once) ...');
|
|
33
30
|
await downloadFile(VC_REDIST, installer);
|
|
34
31
|
}
|
|
35
|
-
if (
|
|
32
|
+
if (!signedBy(installer, SIGNERS.microsoft)) {
|
|
36
33
|
fs.rmSync(installer, { force: true });
|
|
37
34
|
throw new Error('vc_redist.x64.exe is not signed by Microsoft; deleted it (it is downloaded again next boot)');
|
|
38
35
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ToolEnv } from '../tool-env.js';
|
|
2
|
-
import type
|
|
3
|
-
export { BUILD_TOOLS_COMPONENTS, UNREAL_COMPONENTS, visualStudioWanted, type VsRequest } from './vs-request.js';
|
|
2
|
+
import { type VsRequest } from './vs-request.js';
|
|
3
|
+
export { BUILD_TOOLS_COMPONENTS, UNREAL_COMPONENTS, unrealUsesAutosdk, visualStudioWanted, type VsRequest, } from './vs-request.js';
|
|
4
4
|
/** The newest Visual Studio (or Build Tools) with the C++ compiler: its folder and version, or null. */
|
|
5
5
|
export declare function installedVs(): {
|
|
6
6
|
path: string;
|
|
@@ -10,15 +10,20 @@ import path from 'node:path';
|
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
import { spawnAsync } from '../../../shared/download/index.js';
|
|
12
12
|
import { writeJsonAtomic } from '../../../shared/io/json.js';
|
|
13
|
+
import { robocopyFailed } from '../../../shared/io/robocopy-result.js';
|
|
14
|
+
import { safeRemove } from '../../../shared/io/safe-remove.js';
|
|
15
|
+
import { SYSTEM } from '../../../shared/system.js';
|
|
13
16
|
import { execOutput } from '../../runtime/process.js';
|
|
14
17
|
import { exists, mkdirp } from '../../runtime/fs.js';
|
|
15
18
|
import { log, warn } from '../../runtime/log.js';
|
|
19
|
+
import { logsDir } from '../../runtime/paths.js';
|
|
16
20
|
import { duringSession } from '../../runtime/project-context.js';
|
|
17
21
|
import { errMsg } from '../../../core/util/errors.js';
|
|
18
22
|
import { SIGNERS, signedInstaller } from '../installers.js';
|
|
19
|
-
import { assertLocalRoom, freeBytes } from '../persisted.js';
|
|
23
|
+
import { assertLocalRoom, freeBytes, LOCAL_RESERVE_BYTES } from '../persisted.js';
|
|
20
24
|
import { tool } from '../tool-env.js';
|
|
21
|
-
|
|
25
|
+
import { layoutNextStep, vsExitText } from './vs-request.js';
|
|
26
|
+
export { BUILD_TOOLS_COMPONENTS, UNREAL_COMPONENTS, unrealUsesAutosdk, visualStudioWanted, } from './vs-request.js';
|
|
22
27
|
const BOOTSTRAPPERS = {
|
|
23
28
|
community: {
|
|
24
29
|
url: 'https://aka.ms/vs/17/release/vs_community.exe',
|
|
@@ -63,6 +68,61 @@ export function installedVs() {
|
|
|
63
68
|
function layoutDir(product) {
|
|
64
69
|
return path.join(VS_DIR, product === 'community' ? 'layout' : 'layout-buildtools');
|
|
65
70
|
}
|
|
71
|
+
/** The layout arguments of the bootstrapper (the same for the data disk and the staging folder). */
|
|
72
|
+
const layoutArgs = (req, folder) => [
|
|
73
|
+
'--layout',
|
|
74
|
+
folder,
|
|
75
|
+
'--lang',
|
|
76
|
+
'en-US',
|
|
77
|
+
'--quiet',
|
|
78
|
+
'--wait',
|
|
79
|
+
...req.components.flatMap((id) => ['--add', id]),
|
|
80
|
+
];
|
|
81
|
+
/** The bootstrapper's newest log (dd_bootstrapper_*.log) in these folders, or null. */
|
|
82
|
+
function newestBootstrapperLog(folders) {
|
|
83
|
+
const logs = folders.flatMap((dir) => {
|
|
84
|
+
try {
|
|
85
|
+
return fs
|
|
86
|
+
.readdirSync(dir)
|
|
87
|
+
.filter((n) => /^dd_bootstrapper_.*\.log$/i.test(n))
|
|
88
|
+
.map((n) => path.join(dir, n));
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return []; // The folder is not there: no log in it.
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
const stamp = (f) => {
|
|
95
|
+
try {
|
|
96
|
+
return fs.statSync(f).mtimeMs;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return 0; // Gone since the listing.
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
return logs.sort((x, y) => stamp(y) - stamp(x))[0] ?? null;
|
|
103
|
+
}
|
|
104
|
+
/** Says why the layout failed and keeps the installer's log where the sandbox's logs are. */
|
|
105
|
+
function reportLayoutFailure(code, temps) {
|
|
106
|
+
warn('Visual Studio: saving the install files failed (exit ' + code + '): ' + vsExitText(code));
|
|
107
|
+
const found = newestBootstrapperLog(temps);
|
|
108
|
+
if (!found)
|
|
109
|
+
return;
|
|
110
|
+
const kept = path.join(logsDir, 'vs-layout-' + new Date().toISOString().slice(0, 10) + '.log');
|
|
111
|
+
try {
|
|
112
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
113
|
+
fs.copyFileSync(found, kept);
|
|
114
|
+
warn('Visual Studio: the installer log is ' + found + ' (a copy is in ' + kept + ')');
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
warn('Visual Studio: the installer log is ' + found + ' (could not copy it: ' + errMsg(e) + ')');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Copies a finished layout onto the data disk (never /MOV: mapped folders refuse rename-over). */
|
|
121
|
+
async function copyLayout(from, to) {
|
|
122
|
+
mkdirp(to);
|
|
123
|
+
const code = await spawnAsync(SYSTEM.robocopy(), [from, to, '/E', '/COPY:DAT', '/R:1', '/W:1', '/MT:8', '/NP']);
|
|
124
|
+
return !robocopyFailed(code);
|
|
125
|
+
}
|
|
66
126
|
/** The offline copy's installer when it holds every component; made or completed when there is room. */
|
|
67
127
|
async function layoutInstaller(req, bootstrapper) {
|
|
68
128
|
const layout = layoutDir(req.product);
|
|
@@ -79,6 +139,10 @@ async function layoutInstaller(req, bootstrapper) {
|
|
|
79
139
|
return false;
|
|
80
140
|
}
|
|
81
141
|
};
|
|
142
|
+
const finish = () => {
|
|
143
|
+
writeJsonAtomic(marker, { components: [...req.components] });
|
|
144
|
+
return installer;
|
|
145
|
+
};
|
|
82
146
|
if (has())
|
|
83
147
|
return installer;
|
|
84
148
|
if (freeBytes(mkdirp(VS_DIR)) < LAYOUT_ROOM_BYTES) {
|
|
@@ -87,21 +151,36 @@ async function layoutInstaller(req, bootstrapper) {
|
|
|
87
151
|
}
|
|
88
152
|
log('Saving the Visual Studio install files for later boots (~10 GB, once) ...');
|
|
89
153
|
fs.rmSync(marker, { force: true });
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
]);
|
|
99
|
-
|
|
100
|
-
|
|
154
|
+
// (a) The installer writes its scratch files to TEMP and moves them into the layout: a TEMP on the same (mapped)
|
|
155
|
+
// volume, inside the layout's folder tree, keeps those moves on one volume.
|
|
156
|
+
const scratch = mkdirp(path.join(VS_DIR, 'tmp'));
|
|
157
|
+
const code = await spawnAsync(bootstrapper, layoutArgs(req, layout), {
|
|
158
|
+
env: { ...process.env, TEMP: scratch, TMP: scratch },
|
|
159
|
+
});
|
|
160
|
+
if (OK_EXIT_CODES.includes(code) && exists(installer))
|
|
161
|
+
return finish();
|
|
162
|
+
reportLayoutFailure(code, [scratch, os.tmpdir()]);
|
|
163
|
+
// (b) Build it on the sandbox's own disk instead, then copy it over.
|
|
164
|
+
const drive = (process.env.SystemDrive ?? 'C:') + '\\';
|
|
165
|
+
if (layoutNextStep(code, freeBytes(drive), LOCAL_RESERVE_BYTES) !== 'stage') {
|
|
166
|
+
warn('Visual Studio: not enough room on ' + drive + ' to build the install files there; downloading this boot');
|
|
101
167
|
return null;
|
|
102
168
|
}
|
|
103
|
-
|
|
104
|
-
|
|
169
|
+
const staging = path.join(drive, 'vs-layout-staging');
|
|
170
|
+
log('Building the install files on ' + drive + ' and copying them to ' + layout + ' ...');
|
|
171
|
+
const stagedCode = await spawnAsync(bootstrapper, layoutArgs(req, staging));
|
|
172
|
+
const staged = path.join(staging, BOOTSTRAPPERS[req.product].file);
|
|
173
|
+
let kept = false;
|
|
174
|
+
if (OK_EXIT_CODES.includes(stagedCode) && exists(staged)) {
|
|
175
|
+
kept = await copyLayout(staging, layout);
|
|
176
|
+
if (!kept)
|
|
177
|
+
warn('Visual Studio: copying the install files to ' + layout + ' failed');
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
reportLayoutFailure(stagedCode, [os.tmpdir()]);
|
|
181
|
+
}
|
|
182
|
+
safeRemove(staging, drive);
|
|
183
|
+
return kept && exists(installer) ? finish() : null;
|
|
105
184
|
}
|
|
106
185
|
async function install(req, keepInstallFiles) {
|
|
107
186
|
const boot = BOOTSTRAPPERS[req.product];
|
|
@@ -125,7 +204,12 @@ async function install(req, keepInstallFiles) {
|
|
|
125
204
|
...req.components.flatMap((id) => ['--add', id]),
|
|
126
205
|
]);
|
|
127
206
|
if (!OK_EXIT_CODES.includes(code) || !installedVs())
|
|
128
|
-
throw new Error('the Visual Studio installer failed (exit ' +
|
|
207
|
+
throw new Error('the Visual Studio installer failed (exit ' +
|
|
208
|
+
code +
|
|
209
|
+
': ' +
|
|
210
|
+
vsExitText(code) +
|
|
211
|
+
'); its logs are the dd_*.log files in ' +
|
|
212
|
+
os.tmpdir());
|
|
129
213
|
}
|
|
130
214
|
/** Visual Studio as asked for: installed at boot when it is not there yet; its folder and version. */
|
|
131
215
|
export async function ensureVisualStudio(env, req, o) {
|
|
@@ -17,3 +17,24 @@ export interface VsRequest {
|
|
|
17
17
|
readonly accepted: boolean;
|
|
18
18
|
}
|
|
19
19
|
export declare function visualStudioWanted(t: Toolchains, on?: (setting: boolean | 'auto', name: string) => boolean): VsRequest | null;
|
|
20
|
+
/** Whether Unreal is on and builds from the portable MSVC through AutoSDK instead of Visual Studio. */
|
|
21
|
+
export declare function unrealUsesAutosdk(t: Toolchains, on?: (setting: boolean | 'auto', name: string) => boolean): boolean;
|
|
22
|
+
/** The Visual Studio installer's exit code in plain words (it is a uint32: -1 and 4294967295 are the same). */
|
|
23
|
+
export declare function vsExitText(code: number): string;
|
|
24
|
+
/** Room the staging copy of the layout needs on the sandbox's own disk, on top of the reserve. */
|
|
25
|
+
export declare const LAYOUT_STAGING_BYTES: number;
|
|
26
|
+
/**
|
|
27
|
+
* What to do after the layout attempt in the data disk: 'done' when it worked, 'stage' (build it on the sandbox's
|
|
28
|
+
* own disk and copy it over) when it failed and that disk has room, 'skip' (download this boot) otherwise.
|
|
29
|
+
*/
|
|
30
|
+
export declare function layoutNextStep(exit: number | null, localFreeBytes: number, reserveBytes: number): 'done' | 'stage' | 'skip';
|
|
31
|
+
export interface CopyPair {
|
|
32
|
+
readonly from: string;
|
|
33
|
+
readonly to: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The copies that make an Unreal AutoSDK tree from the portable MSVC (copies, not links: mapped folders refuse
|
|
37
|
+
* junctions): MSVC's tools into HostWin64\Win64\VS2022\<msvc> and the Windows SDK's Include, Lib and bin folders into
|
|
38
|
+
* HostWin64\Win64\Windows Kits\10. Pure.
|
|
39
|
+
*/
|
|
40
|
+
export declare function autosdkLayout(msvcRoot: string, msvc: string, sdk: string, autosdkRoot: string): CopyPair[];
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// Which Visual Studio this sandbox installs (a real installer: it runs every boot), from every toolchain that needs
|
|
2
|
+
// one: Unreal (Community with the C++ game workload), Flutter's Windows desktop builds and Unity's Windows IL2CPP
|
|
3
|
+
// builds (the Build Tools' C++ parts). Pure: no runtime, so the self-test loads it. Community holds everything the
|
|
4
|
+
// Build Tools would add, so one install serves all of them.
|
|
5
|
+
import path from 'node:path';
|
|
1
6
|
/**
|
|
2
7
|
* What Epic lists for Unreal Engine 5 with Visual Studio 2022: the game workload, MSVC v143, a Windows SDK of
|
|
3
8
|
* 10.0.18362 or newer, the C++ profiling tools, the .NET Framework targeting packs some engine tools build against,
|
|
@@ -22,7 +27,8 @@ export const BUILD_TOOLS_COMPONENTS = [
|
|
|
22
27
|
/** Whether a toolchain is on (the host resolved "auto"; a session file that still says "auto" counts as off). */
|
|
23
28
|
const isOn = (setting) => setting === true;
|
|
24
29
|
export function visualStudioWanted(t, on = isOn) {
|
|
25
|
-
|
|
30
|
+
// Unreal with visualStudio "autosdk" needs no Visual Studio: it builds from the portable MSVC (autosdk.ts).
|
|
31
|
+
const unreal = on(t.unreal.enabled, 'unreal') && t.unreal.visualStudio === 'full';
|
|
26
32
|
const flutter = on(t.flutter.enabled, 'flutter') && t.flutter.windowsDesktop;
|
|
27
33
|
const unity = on(t.unity.enabled, 'unity') && t.unity.modules.includes('windows-il2cpp');
|
|
28
34
|
const users = [...(unreal ? ['unreal'] : []), ...(flutter ? ['flutter'] : []), ...(unity ? ['unity'] : [])];
|
|
@@ -44,3 +50,56 @@ export function visualStudioWanted(t, on = isOn) {
|
|
|
44
50
|
accepted: t.msvc.acceptLicense,
|
|
45
51
|
};
|
|
46
52
|
}
|
|
53
|
+
/** Whether Unreal is on and builds from the portable MSVC through AutoSDK instead of Visual Studio. */
|
|
54
|
+
export function unrealUsesAutosdk(t, on = isOn) {
|
|
55
|
+
return on(t.unreal.enabled, 'unreal') && t.unreal.visualStudio === 'autosdk';
|
|
56
|
+
}
|
|
57
|
+
/** The Visual Studio installer's exit code in plain words (it is a uint32: -1 and 4294967295 are the same). */
|
|
58
|
+
export function vsExitText(code) {
|
|
59
|
+
const c = code >>> 0;
|
|
60
|
+
switch (c) {
|
|
61
|
+
case 0:
|
|
62
|
+
return 'installed';
|
|
63
|
+
case 3010:
|
|
64
|
+
return 'done; a restart would finish it (not needed here)';
|
|
65
|
+
case 1602:
|
|
66
|
+
return 'cancelled';
|
|
67
|
+
case 5007:
|
|
68
|
+
return 'the installer refused to run (blocked by Windows, for example a restart waiting)';
|
|
69
|
+
case 8006:
|
|
70
|
+
return 'Visual Studio is running and must be closed first';
|
|
71
|
+
case 0xe0434352:
|
|
72
|
+
return '0xE0434352: the Visual Studio installer crashed';
|
|
73
|
+
default:
|
|
74
|
+
return 'the installer failed with exit ' + c + ' (0x' + c.toString(16).toUpperCase() + ')';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Room the staging copy of the layout needs on the sandbox's own disk, on top of the reserve. */
|
|
78
|
+
export const LAYOUT_STAGING_BYTES = 12 * 1024 ** 3;
|
|
79
|
+
/**
|
|
80
|
+
* What to do after the layout attempt in the data disk: 'done' when it worked, 'stage' (build it on the sandbox's
|
|
81
|
+
* own disk and copy it over) when it failed and that disk has room, 'skip' (download this boot) otherwise.
|
|
82
|
+
*/
|
|
83
|
+
export function layoutNextStep(exit, localFreeBytes, reserveBytes) {
|
|
84
|
+
if (exit !== null && (exit === 0 || exit === 3010))
|
|
85
|
+
return 'done';
|
|
86
|
+
return localFreeBytes >= LAYOUT_STAGING_BYTES + reserveBytes ? 'stage' : 'skip';
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The copies that make an Unreal AutoSDK tree from the portable MSVC (copies, not links: mapped folders refuse
|
|
90
|
+
* junctions): MSVC's tools into HostWin64\Win64\VS2022\<msvc> and the Windows SDK's Include, Lib and bin folders into
|
|
91
|
+
* HostWin64\Win64\Windows Kits\10. Pure.
|
|
92
|
+
*/
|
|
93
|
+
export function autosdkLayout(msvcRoot, msvc, sdk, autosdkRoot) {
|
|
94
|
+
const win64 = path.win32.join(autosdkRoot, 'HostWin64', 'Win64');
|
|
95
|
+
return [
|
|
96
|
+
{
|
|
97
|
+
from: path.win32.join(msvcRoot, 'VC', 'Tools', 'MSVC', msvc),
|
|
98
|
+
to: path.win32.join(win64, 'VS2022', msvc),
|
|
99
|
+
},
|
|
100
|
+
...['Include', 'Lib', 'bin'].map((part) => ({
|
|
101
|
+
from: path.win32.join(msvcRoot, 'Windows Kits', '10', part, sdk),
|
|
102
|
+
to: path.win32.join(win64, 'Windows Kits', '10', part, sdk),
|
|
103
|
+
})),
|
|
104
|
+
];
|
|
105
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ToolEnv } from './tool-env.js';
|
|
2
|
-
export declare function notAvailableYet(
|
|
2
|
+
export declare function notAvailableYet(_env: ToolEnv, what: string): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// What a toolchain step whose module is not written yet does: says so in the log
|
|
1
|
+
// What a toolchain step whose module is not written yet does: says so in the log, installs nothing and adds no
|
|
2
|
+
// summary line.
|
|
2
3
|
import { log } from '../runtime/log.js';
|
|
3
|
-
export function notAvailableYet(
|
|
4
|
+
export function notAvailableYet(_env, what) {
|
|
4
5
|
log(what + ': not available yet (nothing installed)');
|
|
5
|
-
env.summary.push(what + ': not available yet');
|
|
6
6
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import crypto from 'node:crypto';
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
+
import { freeBytesAt } from '../../shared/io/disk.js';
|
|
10
11
|
import { readValid, writeJsonAtomic } from '../../shared/io/json.js';
|
|
11
12
|
import { safeRemove } from '../../shared/io/safe-remove.js';
|
|
12
13
|
import { dataDir } from '../runtime/paths.js';
|
|
@@ -27,13 +28,7 @@ function checkSpec(spec) {
|
|
|
27
28
|
}
|
|
28
29
|
/** Free bytes for this user on the disk holding `dir` (0 when it cannot be told). */
|
|
29
30
|
export function freeBytes(dir, deps = {}) {
|
|
30
|
-
|
|
31
|
-
const s = (deps.statfs ?? fs.statfsSync)(dir);
|
|
32
|
-
return s.bavail * s.bsize;
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
return 0;
|
|
36
|
-
}
|
|
31
|
+
return freeBytesAt(dir, deps.statfs) ?? 0;
|
|
37
32
|
}
|
|
38
33
|
const gb = (bytes) => (bytes / GB).toFixed(1) + ' GB';
|
|
39
34
|
/** Throws (with the fix) when the disk holding `dir` has less than `needBytes` free. */
|
|
@@ -21,7 +21,7 @@ import { setupPhp } from './languages/php.js';
|
|
|
21
21
|
import { setupRuby } from './languages/ruby.js';
|
|
22
22
|
import { setupVcRuntime } from './native/vcruntime.js';
|
|
23
23
|
import { setupVscode } from './tools/vscode.js';
|
|
24
|
-
import { ensureVisualStudio, visualStudioWanted } from './native/visual-studio.js';
|
|
24
|
+
import { ensureVisualStudio, unrealUsesAutosdk, visualStudioWanted } from './native/visual-studio.js';
|
|
25
25
|
import { duringSession } from '../runtime/project-context.js';
|
|
26
26
|
import { setupSteam } from './steam/steam.js';
|
|
27
27
|
import { setupGodot } from './engines/godot.js';
|
|
@@ -42,6 +42,8 @@ export function plan(config, versions, o) {
|
|
|
42
42
|
const android = wanted(config.android.enabled, 'android');
|
|
43
43
|
// One Visual Studio for every toolchain that needs the real one (Unreal, Flutter desktop, Unity IL2CPP).
|
|
44
44
|
const vs = visualStudioWanted(config, (setting, name) => wanted(setting, name));
|
|
45
|
+
// Unreal on the portable MSVC (AutoSDK) needs that MSVC even when nothing else asked for it.
|
|
46
|
+
const autosdk = unrealUsesAutosdk(config, (setting, name) => wanted(setting, name));
|
|
45
47
|
const ctx = {
|
|
46
48
|
refresh: o.refresh,
|
|
47
49
|
pcInstalls: o.pcInstalls ?? { items: [] },
|
|
@@ -77,13 +79,16 @@ export function plan(config, versions, o) {
|
|
|
77
79
|
name: 'Unreal',
|
|
78
80
|
enabled: unreal,
|
|
79
81
|
shared: true,
|
|
80
|
-
after: ['Visual Studio'],
|
|
82
|
+
after: ['Visual Studio', 'MSVC'],
|
|
81
83
|
install: (env) => setupUnreal(env, config.unreal, ctx),
|
|
82
84
|
},
|
|
83
85
|
{
|
|
84
86
|
name: 'MSVC',
|
|
85
|
-
enabled: wanted(config.msvc.enabled, 'msvc') && vs === null,
|
|
86
|
-
|
|
87
|
+
enabled: (wanted(config.msvc.enabled, 'msvc') || (autosdk && config.msvc.enabled !== false)) && vs === null,
|
|
88
|
+
// The same Visual Studio licence terms, which Unreal's acceptLicense already names.
|
|
89
|
+
install: (env) => setupMsvc(env, autosdk
|
|
90
|
+
? { ...config.msvc, acceptLicense: config.msvc.acceptLicense || config.unreal.acceptLicense }
|
|
91
|
+
: config.msvc),
|
|
87
92
|
},
|
|
88
93
|
// Rust links with MSVC (msvc host) or MinGW's gcc (GNU host): whichever it uses goes first.
|
|
89
94
|
{
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type AppSigner, type Signature } from '../../shared/signer.js';
|
|
2
|
+
/** The names on a file's valid signature, or null when it is unsigned, tampered with, or cannot be read. */
|
|
3
|
+
export declare function signatureOf(file: string): Signature | null;
|
|
4
|
+
/** Whether a file's valid signature is the expected signer's. */
|
|
5
|
+
export declare function signedBy(file: string, signer: AppSigner): boolean;
|