Manage native build toolchain #1506

Closed
NickGerleman wants to merge 1 commits from export-D52013026 into main
11 changed files with 145 additions and 92 deletions

10
.github/actions/cache-emsdk/action.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
name: Cache the installed copy of emsdk and its build artifacts
runs:
using: "composite"
steps:
- name: Cache emsdk
uses: actions/cache@v3
with:
path: javascript/.emsdk
key: emsdk-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('yarn.lock', 'javascript/package.json', 'javascript/just.config.cjs'}}

View File

@@ -1,23 +0,0 @@
name: Install emsdk (including emcc)
inputs:
version:
description: EMCC Version to install
required: false
default: 3.1.28
runs:
using: "composite"
steps:
- name: Clone emsdk repo
working-directory: ${{ runner.temp }}
shell: bash
run: git clone https://github.com/emscripten-core/emsdk.git
- name: emdsk install
working-directory: ${{ runner.temp }}/emsdk
shell: bash
run: |
./emsdk install ${{ inputs.version }}
./emsdk activate ${{ inputs.version }}
echo $RUNNER_TEMP/emsdk >> $GITHUB_PATH
echo $RUNNER_TEMP/emsdk/upstream/emscripten >> $GITHUB_PATH

View File

@@ -3,12 +3,6 @@ name: Setup JavaScript envirionment
runs: runs:
using: "composite" using: "composite"
steps: steps:
- name: Install emsdk
uses: ./.github/actions/install-emsdk
- name: Install Ninja
uses: ./.github/actions/install-ninja
- name: Setup Node environment - name: Setup Node environment
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:

View File

@@ -24,6 +24,9 @@ jobs:
- name: Setup - name: Setup
uses: ./.github/actions/setup-js uses: ./.github/actions/setup-js
- name: Restore emsdk
uses: ./.github/actions/cache-emsdk
- name: yarn benchmark - name: yarn benchmark
run: yarn benchmark run: yarn benchmark
working-directory: javascript working-directory: javascript
@@ -40,8 +43,11 @@ jobs:
- name: Setup - name: Setup
uses: ./.github/actions/setup-js uses: ./.github/actions/setup-js
- name: Restore emsdk
uses: ./.github/actions/cache-emsdk
- name: yarn build - name: yarn build
run: yarn build run: yarn build --verbose
working-directory: javascript working-directory: javascript
test: test:
@@ -56,6 +62,9 @@ jobs:
- name: Setup - name: Setup
uses: ./.github/actions/setup-js uses: ./.github/actions/setup-js
- name: Restore emsdk
uses: ./.github/actions/cache-emsdk
- name: yarn test - name: yarn test
run: yarn test run: yarn test
working-directory: javascript working-directory: javascript
@@ -94,6 +103,9 @@ jobs:
- name: Setup - name: Setup
uses: ./.github/actions/setup-js uses: ./.github/actions/setup-js
- name: Restore emsdk
uses: ./.github/actions/cache-emsdk
- name: yarn pack - name: yarn pack
run: yarn pack --filename yoga-layout.tar.gz run: yarn pack --filename yoga-layout.tar.gz
working-directory: javascript working-directory: javascript

View File

@@ -34,6 +34,9 @@ jobs:
- name: Setup - name: Setup
uses: ./.github/actions/setup-js uses: ./.github/actions/setup-js
- name: Restore emsdk
uses: ./.github/actions/cache-emsdk
- name: Build Website - name: Build Website
run: yarn build run: yarn build
working-directory: website-next working-directory: website-next

View File

@@ -1,3 +1,4 @@
/.emsdk
/binaries /binaries
/build /build
/dist /dist

View File

@@ -44,7 +44,11 @@ add_link_options(
"SHELL:-s MODULARIZE=1" "SHELL:-s MODULARIZE=1"
"SHELL:-s EXPORT_ES6=1" "SHELL:-s EXPORT_ES6=1"
"SHELL:-s WASM=1" "SHELL:-s WASM=1"
"SHELL:-s TEXTDECODER=0") "SHELL:-s TEXTDECODER=0"
# SINGLE_FILE=1 combined with ENVIRONMENT='web' creates code that works on
# both bundlders and Node.
"SHELL:-s SINGLE_FILE=1"
"SHELL:-s ENVIRONMENT='web'")
link_libraries(embind) link_libraries(embind)
@@ -52,9 +56,4 @@ add_library(yogaObjLib OBJECT ${SOURCES})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/binaries) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/binaries)
add_executable(web $<TARGET_OBJECTS:yogaObjLib>) add_executable(yoga-wasm-base64-esm $<TARGET_OBJECTS:yogaObjLib>)
target_link_options(web PRIVATE
# SINGLE_FILE=1 combined with ENVIRONMENT='web' creates code that works on
# both bundlders and Node.
"SHELL:-s SINGLE_FILE=1"
"SHELL:-s ENVIRONMENT='web'")

View File

@@ -20,9 +20,9 @@ const {
tscTask, tscTask,
} = require('just-scripts'); } = require('just-scripts');
const {readFile, writeFile} = require('fs/promises'); const {existsSync} = require('fs');
const {readFile, writeFile, rm} = require('fs/promises');
const chalk = require('chalk');
const glob = require('glob'); const glob = require('glob');
const path = require('path'); const path = require('path');
const which = require('which'); const which = require('which');
@@ -31,39 +31,26 @@ const node = process.execPath;
option('fix'); option('fix');
task('clean', cleanTask({paths: ['build', 'dist']})); task('clean', cleanTask({paths: ['.emsdk', 'binaries', 'build']}));
function defineFlavor(flavor, env) { task(
task(`cmake-build:${flavor}`, cmakeBuildTask({targets: [flavor]})); 'build',
task( series(installEmsdkTask(), emcmakeGenerateTask(), cmakeBuildTask()),
`jest:${flavor}`, );
task(
'test',
series(
'build',
jestTask({ jestTask({
config: path.join(__dirname, 'jest.config.js'), config: path.join(__dirname, 'jest.config.js'),
nodeArgs: ['--experimental-vm-modules'], nodeArgs: ['--experimental-vm-modules'],
env,
}), }),
);
task(
`test:${flavor}`,
series(emcmakeGenerateTask(), `cmake-build:${flavor}`, `jest:${flavor}`),
);
}
defineFlavor('web');
task('build', series(emcmakeGenerateTask(), cmakeBuildTask()));
task('test', series(emcmakeGenerateTask(), 'cmake-build:web', 'jest:web'));
task(
'benchmark',
series(
emcmakeGenerateTask(),
cmakeBuildTask({targets: ['web']}),
runBenchTask(),
), ),
); );
task('benchmark', series('build', runBenchTask()));
task('clang-format', clangFormatTask({fix: argv().fix})); task('clang-format', clangFormatTask({fix: argv().fix}));
task('prepack-package-json', async () => { task('prepack-package-json', async () => {
@@ -133,36 +120,74 @@ function runBenchTask() {
}; };
} }
function findExecutable(name, failureMessage) { const emsdkVersion = '3.1.28';
const exec = which.sync(name, {nothrow: true}); const emsdkPath = path.join(__dirname, '.emsdk');
if (exec) { const emsdkBin = path.join(
return exec; emsdkPath,
process.platform === 'win32' ? 'emsdk.bat' : 'emsdk',
);
const emcmakeBin = path.join(
emsdkPath,
'upstream',
'emscripten',
process.platform === 'win32' ? 'emcmake.bat' : 'emcmake',
);
function installEmsdkTask() {
return async () => {
if (await isEmsdkReadyAndActivated()) {
logger.verbose(
`emsdk ${emsdkVersion} is already installed and activated`,
);
return false;
} }
logger.error(chalk.bold.red(failureMessage)); logger.info(`installing emsdk ${emsdkVersion} to ${emsdkPath}`);
process.exit(1); await rm(emsdkPath, {recursive: true, force: true});
await spawn(
'git',
['clone', 'https://github.com/emscripten-core/emsdk.git', emsdkPath],
{stdio: 'inherit'},
);
await spawn(emsdkBin, ['install', emsdkVersion], {stdio: 'inherit'});
await spawn(emsdkBin, ['activate', emsdkVersion], {
stdio: logger.enableVerbose ? 'inherit' : 'ignore',
});
};
} }
function tryFindExecutable(name, failureMessage) { async function isEmsdkReadyAndActivated() {
const exec = which.sync(name, {nothrow: true}); if (!existsSync(emcmakeBin)) {
if (exec) { return false;
return exec;
} }
logger.warn(chalk.bold.yellow(failureMessage)); try {
return exec; const emsdkReleases = JSON.parse(
await readFile(path.join(emsdkPath, 'emscripten-releases-tags.json')),
).releases;
const versionHash = emsdkReleases[emsdkVersion];
if (!versionHash) {
return false;
}
const activatedVersion = await readFile(
path.join(emsdkPath, 'upstream', '.emsdk_version'),
);
return activatedVersion.toString().includes(versionHash);
} catch {
// Something is wrong. Pave and redo.
return false;
}
} }
function emcmakeGenerateTask() { function emcmakeGenerateTask() {
return () => { return () => {
const ninja = tryFindExecutable( logger.verbose(`emcmake path: ${emcmakeBin}`);
'ninja',
'Warning: Install Ninja (e.g. "brew install ninja") for faster builds',
);
const emcmake = findExecutable(
'emcmake',
'Error: Please install the emscripten SDK: https://emscripten.org/docs/getting_started/',
);
const args = [ const args = [
'cmake', 'cmake',
@@ -170,20 +195,21 @@ function emcmakeGenerateTask() {
'.', '.',
'-B', '-B',
'build', 'build',
...(ninja ? ['-G', 'Ninja'] : []), ...(process.platform === 'win32' ? [] : ['-G', 'Ninja']),
]; ];
logger.info(['emcmake', ...args].join(' ')); logger.info(['emcmake', ...args].join(' '));
return spawn(emcmake, args, {stdio: 'inherit'}); return spawn(emcmakeBin, args, {
stdio: logger.enableVerbose ? 'inherit' : 'ignore',
});
}; };
} }
function cmakeBuildTask(opts) { function cmakeBuildTask(opts) {
return () => { return () => {
const cmake = findExecutable( const cmake = which.sync('cmake');
'cmake', logger.verbose(`cmake path: ${cmake}`);
'Error: Please install CMake (e.g. "brew install cmake")',
);
const args = [ const args = [
'--build', '--build',
'build', 'build',

View File

@@ -36,11 +36,13 @@
"@types/jest": "^29.5.1", "@types/jest": "^29.5.1",
"@types/node": "^16.18.25", "@types/node": "^16.18.25",
"@types/which": "^3.0.0", "@types/which": "^3.0.0",
"@yogalayout/cmake-bin": "3.28.0-1",
"babel-register-esm": "^1.2.5", "babel-register-esm": "^1.2.5",
"clang-format": "^1.8.0", "clang-format": "^1.8.0",
"glob": "^8.0.3", "glob": "^8.0.3",
"jest": "^29.3.1", "jest": "^29.3.1",
"just-scripts": "^2.1.0", "just-scripts": "^2.1.0",
"ninja-binaries": "^1.11.1",
"which": "^3.0.0" "which": "^3.0.0"
} }
} }

View File

@@ -8,7 +8,7 @@
*/ */
// @ts-ignore untyped from Emscripten // @ts-ignore untyped from Emscripten
import loadYoga from '../binaries/web.js'; import loadYoga from '../binaries/yoga-wasm-base64-esm.js';
import wrapAssembly from './wrapAssembly.ts'; import wrapAssembly from './wrapAssembly.ts';
export type { export type {

View File

@@ -2901,6 +2901,30 @@
resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
"@yogalayout/cmake-bin-darwin-universal@3.28.0":
version "3.28.0"
resolved "https://registry.yarnpkg.com/@yogalayout/cmake-bin-darwin-universal/-/cmake-bin-darwin-universal-3.28.0.tgz#727e10d62ae4fca960e3502465b68f948841dd6c"
integrity sha512-grgtVOaJZT5fCvCaLVbYmVKqsBqtphQffG6WTjDCdyJ++giM4AvrhiRgFKnyr4rx3pj42DrWTvpIWJJl2nBc6w==
"@yogalayout/cmake-bin-linux-x64@3.28.0":
version "3.28.0"
resolved "https://registry.yarnpkg.com/@yogalayout/cmake-bin-linux-x64/-/cmake-bin-linux-x64-3.28.0.tgz#9bbd414a24a1264a6b8b824c6eb582d623d4ae8e"
integrity sha512-RQlsWl1si7+yi87NI2tV13Ca7RYxhjw1AmoULLwhPNkNSLu9Laja2IFvjgvz6iJ3zD9fWSMUl5TwA/ugiNj7OQ==
"@yogalayout/cmake-bin-windows-x64@3.28.0":
version "3.28.0"
resolved "https://registry.yarnpkg.com/@yogalayout/cmake-bin-windows-x64/-/cmake-bin-windows-x64-3.28.0.tgz#c60787a7148d54fe0d579dcb4fd80d1f7aef56ed"
integrity sha512-wMWwUbw/NkXynVRcrFElbFJD/p9HQHmScuv9amZAKMM/JhLjrxDQaivQ+7ZmEOlRQ1uWZjiue9QCkaJERt9Bog==
"@yogalayout/cmake-bin@3.28.0-1":
version "3.28.0-1"
resolved "https://registry.yarnpkg.com/@yogalayout/cmake-bin/-/cmake-bin-3.28.0-1.tgz#af366eb6205ec07c1ede2bcec668a2be083869b9"
integrity sha512-2bnXhH4qt+bxjm+fgkjTHYhvB2quaS874XkYmUL1Obo6S0kWtDXEp7s0ZgtKvOfW3+y4Q5eomNMIobOeGLi9rw==
optionalDependencies:
"@yogalayout/cmake-bin-darwin-universal" "3.28.0"
"@yogalayout/cmake-bin-linux-x64" "3.28.0"
"@yogalayout/cmake-bin-windows-x64" "3.28.0"
accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8:
version "1.3.8" version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
@@ -7823,6 +7847,11 @@ next-tick@^1.1.0:
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
ninja-binaries@^1.11.1:
version "1.11.1"
resolved "https://registry.yarnpkg.com/ninja-binaries/-/ninja-binaries-1.11.1.tgz#9089191512ab44f990b1ee03792b47f79aedeaaa"
integrity sha512-/fjozfApkgrHe2IGbSMhwiDYdz0AUpvsm1szWWu9cy4NJmwPDuhd4mvfFMlCp35CPexHim3jSWn1BjHddxFkkA==
no-case@^3.0.4: no-case@^3.0.4:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"