Skip to content

Commit

Permalink
[unity] support OSX universal build
Browse files Browse the repository at this point in the history
  • Loading branch information
zombieyang committed Sep 4, 2023
1 parent aec7dc7 commit 4132867
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 38 deletions.
81 changes: 81 additions & 0 deletions unity/Assets/core/upm/Plugins/macOS/libnode.93.dylib.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

81 changes: 81 additions & 0 deletions unity/Assets/core/upm/Plugins/macOS/puerts.bundle.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions unity/Assets/core/upm/Plugins/macOS/puerts_il2cpp.bundle.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 35 additions & 26 deletions unity/cli/cmd.mts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Option, program } from "commander";
import { join } from "path";
import downloadBackend from "./backend.mjs";
import { dotnetTest, unityTest } from "./test.mjs";
import runPuertsMake, { platformCompileConfig } from "./make.mjs";
import runPuertsMake, { makeOSXUniveralBinary, platformCompileConfig } from "./make.mjs";

setWinCMDEncodingToUTF8();

Expand All @@ -21,12 +21,12 @@ program
.choices(["win", "osx", "linux", "android", "ios"])
)
.addOption(
new Option("--arch <arch>", "the target architecture")
new Option("--arch <arch>", "the target architecture. 'auto' means build all available archs for the platform and universal binary will be created in osx.")
.default("auto")
.choices(["auto", "ia32", "x64", "arm64", "armv7"])
)
.addOption(
new Option("--config <ReleaseOrDebug>", "the target architecture")
new Option("--config <ReleaseOrDebug>", "Debug ver or Release ver. In Windows, Debug means DebugWithRelInfo")
.default("Release")
.choices(["Release", "Debug"])
)
Expand All @@ -37,28 +37,7 @@ program
let platform = options.platform;
let arch = options.arch;

if (!quickcommand) {
if (options.platform && options.arch == 'auto') {
let promiseChain = Promise.resolve();
Object.keys((platformCompileConfig as any)[options.platform]).forEach(arch => {
promiseChain = promiseChain.then(function () {
//@ts-ignore
options.arch = arch;
return runPuertsMake(cwd, options)
})
});

} else if (!options.platform && options.arch == 'auto') {
options.platform = (nodePlatformToPuerPlatform as any)[process.platform]
//@ts-ignore
options.arch = process.arch;
runPuertsMake(cwd, options);

} else {
runPuertsMake(cwd, options);
}

} else {
if (quickcommand) {
switch (quickcommand[0]) {
case 'v':
backend = 'v8_9.4'; break;
Expand Down Expand Up @@ -96,6 +75,8 @@ program
arch = 'armv7'; break;
case '8':
arch = 'arm64'; break;
case '0':
arch = 'auto'; break;

default:
throw new Error(`invalid command[2] : ${quickcommand[2]}`);
Expand All @@ -110,7 +91,35 @@ program
}

console.log('quick command parse result:', { backend, config, arch, platform });
runPuertsMake(cwd, { backend, config, arch, platform });
options = { backend, config, arch, platform };

}

if (options.platform && options.arch == 'auto') {
let promiseChain = Promise.resolve();
const outputs: string[][] = []
Object.keys((platformCompileConfig as any)[options.platform]).forEach(arch => {
promiseChain = promiseChain.then(function () {
//@ts-ignore
options.arch = arch;
return runPuertsMake(cwd, options).then(res => {
outputs.push(res);
})
})
});
if (options.platform == 'osx') {
// if arch is not specified, make universal binary
promiseChain = promiseChain.then(() => makeOSXUniveralBinary(cwd, outputs));
}

} else if (!options.platform && options.arch == 'auto') {
options.platform = (nodePlatformToPuerPlatform as any)[process.platform]
//@ts-ignore
options.arch = process.arch;
runPuertsMake(cwd, options);

} else {
runPuertsMake(cwd, options);
}
})

Expand Down
48 changes: 36 additions & 12 deletions unity/cli/make.mts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { existsSync, readFileSync } from "fs";
import { cd, cp, exec, mkdir, mv } from "@puerts/shell-util"
import { join, normalize } from "path";
import { cd, cp, exec, mkdir, mv, rm } from "@puerts/shell-util"
import { basename, join, normalize } from "path";
import assert from "assert";
import downloadBackend from "./backend.mjs";
import { createRequire } from "module";
Expand Down Expand Up @@ -29,9 +29,9 @@ const platformCompileConfig = {
assert.equal(0, exec(`cmake ${cmakeDArgs} -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DJS_ENGINE=${options.backend} -DCMAKE_BUILD_TYPE=${options.config} -DANDROID_ABI=${ABI} -H. -B${CMAKE_BUILD_PATH} -DCMAKE_TOOLCHAIN_FILE=${NDK}/build/cmake/android.toolchain.cmake -DANDROID_NATIVE_API_LEVEL=${API} -DANDROID_TOOLCHAIN=clang -DANDROID_TOOLCHAIN_NAME=${TOOLCHAIN_NAME}`).code)
assert.equal(0, exec(`cmake --build ${CMAKE_BUILD_PATH} --config ${options.config}`).code)

if (existsSync(`${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.a`))
if (existsSync(`${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.a`))
return [`${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.a`]
else
else
return [`${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.so`, `${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.stripped.so~`]
}
},
Expand All @@ -46,9 +46,9 @@ const platformCompileConfig = {
assert.equal(0, exec(`cmake ${cmakeDArgs} -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DJS_ENGINE=${options.backend} -DCMAKE_BUILD_TYPE=${options.config} -DANDROID_ABI=${ABI} -H. -B${CMAKE_BUILD_PATH} -DCMAKE_TOOLCHAIN_FILE=${NDK}/build/cmake/android.toolchain.cmake -DANDROID_NATIVE_API_LEVEL=${API} -DANDROID_TOOLCHAIN=clang -DANDROID_TOOLCHAIN_NAME=${TOOLCHAIN_NAME}`).code)
assert.equal(0, exec(`cmake --build ${CMAKE_BUILD_PATH} --config ${options.config}`).code)

if (existsSync(`${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.a`))
if (existsSync(`${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.a`))
return [`${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.a`]
else
else
return [`${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.so`, `${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.stripped.so~`]
}
},
Expand All @@ -63,9 +63,9 @@ const platformCompileConfig = {
assert.equal(0, exec(`cmake ${cmakeDArgs} -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DJS_ENGINE=${options.backend} -DCMAKE_BUILD_TYPE=${options.config} -DANDROID_ABI=${ABI} -H. -B${CMAKE_BUILD_PATH} -DCMAKE_TOOLCHAIN_FILE=${NDK}/build/cmake/android.toolchain.cmake -DANDROID_NATIVE_API_LEVEL=${API} -DANDROID_TOOLCHAIN=clang -DANDROID_TOOLCHAIN_NAME=${TOOLCHAIN_NAME}`).code)
assert.equal(0, exec(`cmake --build ${CMAKE_BUILD_PATH} --config ${options.config}`).code)

if (existsSync(`${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.a`))
if (existsSync(`${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.a`))
return [`${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.a`]
else
else
return [`${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.so`, `${CMAKE_BUILD_PATH}/lib${cmakeAddedLibraryName}.stripped.so~`]
}
}
Expand Down Expand Up @@ -171,9 +171,7 @@ async function runPuertsMake(cwd: string, options: BuildOptions) {

const BuildConfig = (platformCompileConfig as any)[options.platform][options.arch];
const CMAKE_BUILD_PATH = cwd + `/build_${options.platform}_${options.arch}_${options.backend}${options.config != "Release" ? "_debug" : ""}`
const OUTPUT_PATH = cmakeAddedLibraryName == "puerts_il2cpp" ?
cwd + '/../Assets/core/upm/Plugins/' + BuildConfig.outputPluginPath :
cwd + '/../Assets/core/upm/Plugins/' + BuildConfig.outputPluginPath;
const OUTPUT_PATH = cwd + '/../Assets/core/upm/Plugins/' + BuildConfig.outputPluginPath;
const BackendConfig = JSON.parse(readFileSync(cwd + `/cmake/backends.json`, 'utf-8'))[options.backend]?.config;

if (BackendConfig?.skip?.[options.platform]?.[options.arch]) {
Expand Down Expand Up @@ -215,5 +213,31 @@ async function runPuertsMake(cwd: string, options: BuildOptions) {
return copyConfig;
}

async function makeOSXUniveralBinary(cwd: string, copyConfig: string[][]): Promise<void> {
const OUTPUT_PATH = cwd + '/../Assets/core/upm/Plugins/macOS';
const cmakeAddedLibraryName = readFileSync(`${cwd}/CMakeLists.txt`, 'utf-8').match(/add_library\((\w*)/)[1];

if (copyConfig.length != 2) throw new Error(`makeOSXUniveralBinary can only combine 2 archs(arm64, x64) now, but got ${copyConfig.length}`);

for (let i = 0; i < copyConfig[0].length; i++) {
for (let j = 0; j < copyConfig[1].length; j++) {
if (basename(copyConfig[0][i]) == basename(copyConfig[1][j])) {
assert.equal(0, exec(`lipo -create -output ${join(OUTPUT_PATH, basename(copyConfig[0][i]))} ${copyConfig[0][i]} ${copyConfig[1][j]}`).code)
copyConfig[0].splice(i, 1); i--;
copyConfig[1].splice(j, 1); j--;
break;
}
}
}

if (copyConfig[0].length != 1 && copyConfig[1].length != 1) throw new Error(`makeOSXUniveralBinary error: too many binary still need to lipo: ${copyConfig}`)
assert.equal(0, exec(`lipo -create -output ${join(OUTPUT_PATH, cmakeAddedLibraryName + '.bundle')} ${copyConfig[0][0]} ${copyConfig[1][0]}`).code)

rm('-rf', cwd + '/../Assets/core/upm/Plugins/' + platformCompileConfig.osx.arm64.outputPluginPath + "/*.dylib");
rm('-rf', cwd + '/../Assets/core/upm/Plugins/' + platformCompileConfig.osx.arm64.outputPluginPath + "/*.bundle");
rm('-rf', cwd + '/../Assets/core/upm/Plugins/' + platformCompileConfig.osx.x64.outputPluginPath + "/*.dylib");
rm('-rf', cwd + '/../Assets/core/upm/Plugins/' + platformCompileConfig.osx.x64.outputPluginPath + "/*.bundle");
}

export default runPuertsMake;
export { platformCompileConfig }
export { platformCompileConfig, makeOSXUniveralBinary }

1 comment on commit 4132867

@zombieyang
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Unity3D, if we don't use universal binary, a DLLNotFoundExeception will be thrown in Standalone Mac Silicon build.
企业微信截图_2da53ef0-5304-49b4-b9cd-3624b344818d(1)

Please sign in to comment.