Add Win32 metadata all executable binaries (#296710)

This commit is contained in:
Dmitriy Vasyura
2026-02-24 00:29:02 -08:00
committed by GitHub
parent 81b9086751
commit d0f203e76f
5 changed files with 114 additions and 7 deletions

View File

@@ -25,6 +25,8 @@ import { untar } from './lib/util.ts';
import File from 'vinyl';
import * as fs from 'fs';
import glob from 'glob';
import { promisify } from 'util';
import rceditCallback from 'rcedit';
import { compileBuildWithManglingTask } from './gulpfile.compile.ts';
import { cleanExtensionsBuildTask, compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileExtensionMediaBuildTask } from './gulpfile.extensions.ts';
import { vscodeWebResourceIncludes, createVSCodeWebFileContentMapper } from './gulpfile.vscode.web.ts';
@@ -35,6 +37,8 @@ import { fetchUrls, fetchGithub } from './lib/fetch.ts';
import jsonEditor from 'gulp-json-editor';
const rcedit = promisify(rceditCallback);
const REPO_ROOT = path.dirname(import.meta.dirname);
const commit = getVersion(REPO_ROOT);
const BUILD_ROOT = path.dirname(REPO_ROOT);
@@ -422,6 +426,40 @@ function packageTask(type: string, platform: string, arch: string, sourceFolderN
};
}
function patchWin32DependenciesTask(destinationFolderName: string) {
const cwd = path.join(BUILD_ROOT, destinationFolderName);
return async () => {
const deps = (await Promise.all([
promisify(glob)('**/*.node', { cwd }),
promisify(glob)('**/rg.exe', { cwd }),
])).flatMap(o => o);
const packageJsonContents = JSON.parse(await fs.promises.readFile(path.join(cwd, 'package.json'), 'utf8'));
const productContents = JSON.parse(await fs.promises.readFile(path.join(cwd, 'product.json'), 'utf8'));
const baseVersion = packageJsonContents.version.replace(/-.*$/, '');
const patchPromises = deps.map<Promise<unknown>>(async dep => {
const basename = path.basename(dep);
await rcedit(path.join(cwd, dep), {
'file-version': baseVersion,
'version-string': {
'CompanyName': 'Microsoft Corporation',
'FileDescription': productContents.nameLong,
'FileVersion': packageJsonContents.version,
'InternalName': basename,
'LegalCopyright': 'Copyright (C) 2026 Microsoft. All rights reserved',
'OriginalFilename': basename,
'ProductName': productContents.nameLong,
'ProductVersion': packageJsonContents.version,
}
});
});
await Promise.all(patchPromises);
};
}
/**
* @param product The parsed product.json file contents
*/
@@ -466,12 +504,18 @@ function tweakProductForServerWeb(product: typeof import('../product.json')) {
const sourceFolderName = `out-vscode-${type}${dashed(minified)}`;
const destinationFolderName = `vscode-${type}${dashed(platform)}${dashed(arch)}`;
const serverTaskCI = task.define(`vscode-${type}${dashed(platform)}${dashed(arch)}${dashed(minified)}-ci`, task.series(
const packageTasks: task.Task[] = [
compileNativeExtensionsBuildTask,
gulp.task(`node-${platform}-${arch}`) as task.Task,
util.rimraf(path.join(BUILD_ROOT, destinationFolderName)),
packageTask(type, platform, arch, sourceFolderName, destinationFolderName)
));
];
if (platform === 'win32') {
packageTasks.push(patchWin32DependenciesTask(destinationFolderName));
}
const serverTaskCI = task.define(`vscode-${type}${dashed(platform)}${dashed(arch)}${dashed(minified)}-ci`, task.series(...packageTasks));
gulp.task(serverTaskCI);
const serverTask = task.define(`vscode-${type}${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series(

View File

@@ -623,12 +623,16 @@ function patchWin32DependenciesTask(destinationFolderName: string) {
const cwd = path.join(path.dirname(root), destinationFolderName);
return async () => {
const deps = await glob('**/*.node', { cwd, ignore: 'extensions/node_modules/@parcel/watcher/**' });
const deps = (await Promise.all([
glob('**/*.node', { cwd, ignore: 'extensions/node_modules/@parcel/watcher/**' }),
glob('**/rg.exe', { cwd }),
glob('**/*explorer_command*.dll', { cwd }),
])).flatMap(o => o);
const packageJson = JSON.parse(await fs.promises.readFile(path.join(cwd, versionedResourcesFolder, 'resources', 'app', 'package.json'), 'utf8'));
const product = JSON.parse(await fs.promises.readFile(path.join(cwd, versionedResourcesFolder, 'resources', 'app', 'product.json'), 'utf8'));
const baseVersion = packageJson.version.replace(/-.*$/, '');
await Promise.all(deps.map(async dep => {
const patchPromises = deps.map<Promise<unknown>>(async dep => {
const basename = path.basename(dep);
await rcedit(path.join(cwd, dep), {
@@ -638,13 +642,15 @@ function patchWin32DependenciesTask(destinationFolderName: string) {
'FileDescription': product.nameLong,
'FileVersion': packageJson.version,
'InternalName': basename,
'LegalCopyright': 'Copyright (C) 2022 Microsoft. All rights reserved',
'LegalCopyright': 'Copyright (C) 2026 Microsoft. All rights reserved',
'OriginalFilename': basename,
'ProductName': product.nameLong,
'ProductVersion': packageJson.version,
}
});
}));
});
await Promise.all(patchPromises);
};
}

View File

@@ -58,6 +58,7 @@ tar = "0.4.38"
[build-dependencies]
serde = { version="1.0.163", features = ["derive"] }
serde_json = "1.0.96"
winresource = "0.1"
[target.'cfg(windows)'.dependencies]
winreg = "0.50.0"

View File

@@ -20,6 +20,7 @@ fn main() {
let files = enumerate_source_files().expect("expected to enumerate files");
ensure_file_headers(&files).expect("expected to ensure file headers");
apply_build_environment_variables();
apply_win32_version_resources();
}
fn camel_case_to_constant_case(key: &str) -> String {
@@ -148,6 +149,56 @@ fn apply_build_environment_variables() {
};
}
fn apply_win32_version_resources() {
if env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") {
return;
}
let repo_dir = env::current_dir().unwrap().join("..");
let package_json = read_json_from_path::<PackageJson>(&repo_dir.join("package.json"));
let product_json_path = match env::var("VSCODE_CLI_PRODUCT_JSON") {
Ok(v) => {
if cfg!(windows) {
PathBuf::from_str(&v.replace('/', "\\")).unwrap()
} else {
PathBuf::from_str(&v).unwrap()
}
}
Err(_) => repo_dir.join("product.json"),
};
let product: HashMap<String, Value> = read_json_from_path(&product_json_path);
let name_long = product
.get("nameLong")
.and_then(|v| v.as_str())
.unwrap_or("Code - OSS");
let application_name = product
.get("applicationName")
.and_then(|v| v.as_str())
.unwrap_or("code");
let exe_name = format!("{application_name}.exe");
let base_version = package_json.version.split('-').next().unwrap_or("0.0.0");
let version_parts: Vec<&str> = base_version.split('.').collect();
let major: u64 = version_parts.first().and_then(|v| v.parse().ok()).unwrap_or(0);
let minor: u64 = version_parts.get(1).and_then(|v| v.parse().ok()).unwrap_or(0);
let patch: u64 = version_parts.get(2).and_then(|v| v.parse().ok()).unwrap_or(0);
let mut res = winresource::WindowsResource::new();
res.set("ProductName", name_long);
res.set("FileDescription", name_long);
res.set("CompanyName", "Microsoft Corporation");
res.set("LegalCopyright", "Copyright (C) 2026 Microsoft. All rights reserved");
res.set("FileVersion", &package_json.version);
res.set("ProductVersion", &package_json.version);
res.set("InternalName", &exe_name);
res.set("OriginalFilename", &exe_name);
res.set_version_info(winresource::VersionInfo::FILEVERSION, (major << 48) | (minor << 16) | patch);
res.set_version_info(winresource::VersionInfo::PRODUCTVERSION, (major << 48) | (minor << 16) | patch);
res.compile().expect("failed to compile Windows resources");
}
fn ensure_file_headers(files: &[PathBuf]) -> Result<(), io::Error> {
let mut ok = true;

View File

@@ -33,6 +33,7 @@ interface ITargetMetadata {
export class TestContext {
private static readonly authenticodeInclude = /^.+\.(exe|dll|sys|cab|cat|msi|jar|ocx|ps1|psm1|psd1|ps1xml|pssc1)$/i;
private static readonly versionInfoInclude = /^.+\.(exe|dll|node|msi)$/i;
private static readonly versionInfoExclude = /^(dxil\.dll|ffmpeg\.dll|msalruntime\.dll)$/i;
private readonly tempDirs = new Set<string>();
private readonly wslTempDirs = new Set<string>();
@@ -404,7 +405,11 @@ export class TestContext {
if (entry.isDirectory()) {
this.collectVersionInfoFiles(filePath, files);
} else if (TestContext.versionInfoInclude.test(entry.name)) {
files.push(filePath);
if (TestContext.versionInfoExclude.test(entry.name)) {
this.log(`Skipping excluded file from VersionInfo validation: ${filePath}`);
} else {
files.push(filePath);
}
}
}
}