Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
174 changes: 174 additions & 0 deletions .ado/scripts/build-hermesc-windows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#!/usr/bin/env node
const path = require('path');
const fs = require('fs');
const https = require('https');
const {spawnSync} = require('child_process');

const log = (msg) => process.stdout.write(`${msg}\n`);

const REPO_ROOT = path.resolve(__dirname, '..', '..');
const BASE_DIR = process.env.HERMES_WS_DIR || path.join(REPO_ROOT, 'build', 'win_hermesc');
const ICU_URL = process.env.ICU_URL || 'https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-Win64-MSVC2017.zip';
const ICU_DIR = path.join(BASE_DIR, 'icu');
const DEPS_DIR = path.join(BASE_DIR, 'deps');
const WIN64_BIN_DIR = path.join(BASE_DIR, 'win64-bin');
const CMAKE_BUILD_DIR = path.join(BASE_DIR, 'build_release');

const PATH_KEY = Object.keys(process.env).find((key) => key.toLowerCase() === 'path') || 'PATH';
const extraPath = [process.env.CMAKE_DIR, process.env.MSBUILD_DIR].filter(Boolean);
if (extraPath.length) {
const currentPath = process.env[PATH_KEY] || '';
const additions = extraPath.join(path.delimiter);
process.env[PATH_KEY] = additions + (currentPath ? `${path.delimiter}${currentPath}` : '');
log(`Extended PATH with: ${additions}`);
}

function ensureDir(dir) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, {recursive: true});
}
}

function ensureWorkspace() {
[BASE_DIR, path.join(BASE_DIR, 'osx-bin'), ICU_DIR, DEPS_DIR, WIN64_BIN_DIR].forEach(ensureDir);
}

function downloadFile(url, dest) {
return new Promise((resolve, reject) => {
if (fs.existsSync(dest)) {
fs.unlinkSync(dest);
}
const file = fs.createWriteStream(dest);
https.get(url, (response) => {
if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
downloadFile(response.headers.location, dest).then(resolve).catch(reject);
return;
}
if (response.statusCode !== 200) {
reject(new Error(`Download failed with status ${response.statusCode}`));
return;
}
response.pipe(file);
file.on('finish', () => {
file.close(resolve);
});
}).on('error', (err) => {
fs.unlink(dest, () => reject(err));
});
});
}

function run(command, args, options = {}) {
log(`> ${command} ${(args || []).join(' ')}`);
const result = spawnSync(command, args, {
stdio: 'inherit',
shell: false,
...options,
});
if (result.error) {
if (result.error.code === 'ENOENT') {
throw new Error(`Command not found: ${command}. Update PATH or set CMAKE_DIR/MSBUILD_DIR.`);
}
throw new Error(`Failed to launch ${command}: ${result.error.message}`);
}
if (result.status !== 0) {
throw new Error(`${command} exited with code ${result.status}`);
}
}

function expandZip(zipPath, destination) {
run('powershell.exe', [
'-NoLogo',
'-NoProfile',
'-Command',
`Expand-Archive -Path '${zipPath}' -DestinationPath '${destination}' -Force`,
]);
}

function copyFile(source, destination) {
ensureDir(path.dirname(destination));
fs.copyFileSync(source, destination);
}

function copyPattern(sourceDir, pattern, destinationDir) {
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
for (const entry of fs.readdirSync(sourceDir)) {
if (regex.test(entry)) {
copyFile(path.join(sourceDir, entry), path.join(destinationDir, entry));
}
}
}

async function fetchIcu() {
log('Downloading ICU');
const zipPath = path.join(ICU_DIR, 'icu.zip');
await downloadFile(ICU_URL, zipPath);
log('Expanding ICU');
expandZip(zipPath, ICU_DIR);
}

function copyRuntimeDependencies() {
copyPattern(path.join(ICU_DIR, 'bin64'), '^icu.*\.dll$', DEPS_DIR);
const system32 = path.join(process.env.windir || 'C:/Windows', 'System32');
['msvcp140.dll', 'vcruntime140.dll', 'vcruntime140_1.dll'].forEach((dll) => {
const source = path.join(system32, dll);
if (!fs.existsSync(source)) {
throw new Error(`Missing runtime dependency: ${source}`);
}
copyFile(source, path.join(DEPS_DIR, dll));
});
}

function ensureCMakeBuild() {
const configureArgs = [
'-S',
'.',
'-B',
CMAKE_BUILD_DIR,
'-G',
'Visual Studio 17 2022',
'-Ax64',
'-DCMAKE_BUILD_TYPE=Release',
'-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=True',
'-DHERMES_ENABLE_WIN10_ICU_FALLBACK=OFF',
];
run('cmake', configureArgs, {
env: {
...process.env,
ICU_ROOT: ICU_DIR,
},
});
run('cmake', ['--build', CMAKE_BUILD_DIR, '--target', 'hermesc', '--config', 'Release'], {
env: {
...process.env,
BOOST_CONTEXT_MASM: 'OFF',
},
});
}

function stageArtifacts() {
const hermescSource = path.join(CMAKE_BUILD_DIR, 'bin', 'Release', 'hermesc.exe');
if (!fs.existsSync(hermescSource)) {
throw new Error(`Build output missing: ${hermescSource}`);
}
copyFile(hermescSource, path.join(WIN64_BIN_DIR, 'hermesc.exe'));
for (const entry of fs.readdirSync(DEPS_DIR)) {
copyFile(path.join(DEPS_DIR, entry), path.join(WIN64_BIN_DIR, entry));
}
}

async function main() {
try {
ensureWorkspace();
await fetchIcu();
copyRuntimeDependencies();
ensureCMakeBuild();
stageArtifacts();
log('HermesC build artifacts staged to win64-bin');
} catch (error) {
log(`Build failed: ${error.message}`);
process.exitCode = 1;
}
}

main();
36 changes: 36 additions & 0 deletions .ado/scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,22 @@ function cmakeConfigure(buildParams) {
if (targetTriple) {
genArgs.push(`-DCMAKE_C_FLAGS="-target ${targetTriple}"`);
genArgs.push(`-DCMAKE_CXX_FLAGS="-target ${targetTriple}"`);
genArgs.push(`-DCMAKE_ASM_FLAGS="-target ${targetTriple}"`);
}
}

// For ARM64/ARM64EC cross-compilation, CMAKE_SYSTEM_PROCESSOR must be set
// so that Boost.Context selects ARM64 assembly files instead of defaulting
// to x86_64 based on the host processor (AMD64). This applies to both
// Clang and MSVC builds.
if (platform === "arm64" || platform === "arm64ec") {
genArgs.push("-DCMAKE_SYSTEM_PROCESSOR=ARM64");

// MSVC ARM64 assembler is called armasm64, not armasm (which is ARM32).
// CMake's ASM_ARMASM language detection looks for armasm by default,
// so we must point it to the correct executable.
if (msvc) {
genArgs.push("-DCMAKE_ASM_MARMASM_COMPILER=armasm64");
}
}

Expand Down Expand Up @@ -456,6 +472,26 @@ function cmakeConfigure(buildParams) {
}`,
);

// Static Hermes (shermes) invokes an external C compiler at runtime to
// compile generated C code. The default is "cc" which doesn't exist on
// Windows. Use "clang" since the generated code uses GCC-style flags.
genArgs.push('-DSHERMES_CC=clang');

// When cross-compiling (e.g. x86 on x64 host), shermes must also target the
// correct architecture when invoking clang at runtime. The main build gets
// this via CMAKE_C_FLAGS, but shermes uses its own SHERMES_CC_SYSCFLAGS.
if (platform !== hostCpuArch && !msvc) {
let shermesTarget = "";
if (platform === "x86") {
shermesTarget = "i686-pc-windows-msvc";
} else if (platform === "arm64") {
shermesTarget = "aarch64-pc-windows-msvc";
}
if (shermesTarget) {
genArgs.push(`-DSHERMES_CC_SYSCFLAGS="-target ${shermesTarget}"`);
}
}

runCMakeCommand(`cmake ${genArgs.join(" ")} "${sourcesPath}"`, buildParams);
}

Expand Down
Loading