123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.DmgTarget = void 0;
- const app_builder_lib_1 = require("app-builder-lib");
- const macCodeSign_1 = require("app-builder-lib/out/codeSign/macCodeSign");
- const differentialUpdateInfoBuilder_1 = require("app-builder-lib/out/targets/differentialUpdateInfoBuilder");
- const appBuilder_1 = require("app-builder-lib/out/util/appBuilder");
- const filename_1 = require("app-builder-lib/out/util/filename");
- const builder_util_1 = require("builder-util");
- const fs_1 = require("builder-util/out/fs");
- const fs_extra_1 = require("fs-extra");
- const path = require("path");
- const dmgLicense_1 = require("./dmgLicense");
- const dmgUtil_1 = require("./dmgUtil");
- const os_1 = require("os");
- class DmgTarget extends app_builder_lib_1.Target {
- constructor(packager, outDir) {
- super("dmg");
- this.packager = packager;
- this.outDir = outDir;
- this.options = this.packager.config.dmg || Object.create(null);
- }
- async build(appPath, arch) {
- const packager = this.packager;
- // tslint:disable-next-line:no-invalid-template-strings
- const artifactName = packager.expandArtifactNamePattern(this.options, "dmg", arch, "${productName}-" + (packager.platformSpecificBuildOptions.bundleShortVersion || "${version}") + "-${arch}.${ext}", true, packager.platformSpecificBuildOptions.defaultArch);
- const artifactPath = path.join(this.outDir, artifactName);
- await packager.info.callArtifactBuildStarted({
- targetPresentableName: "DMG",
- file: artifactPath,
- arch,
- });
- const volumeName = (0, filename_1.sanitizeFileName)(this.computeVolumeName(arch, this.options.title));
- const tempDmg = await createStageDmg(await packager.getTempFile(".dmg"), appPath, volumeName);
- const specification = await this.computeDmgOptions();
- // https://github.com/electron-userland/electron-builder/issues/2115
- const backgroundFile = specification.background == null ? null : await transformBackgroundFileIfNeed(specification.background, packager.info.tempDirManager);
- const finalSize = await computeAssetSize(packager.info.cancellationToken, tempDmg, specification, backgroundFile);
- const expandingFinalSize = finalSize * 0.1 + finalSize;
- await (0, builder_util_1.exec)("hdiutil", ["resize", "-size", expandingFinalSize.toString(), tempDmg]);
- const volumePath = path.join("/Volumes", volumeName);
- if (await (0, fs_1.exists)(volumePath)) {
- builder_util_1.log.debug({ volumePath }, "unmounting previous disk image");
- await (0, dmgUtil_1.detach)(volumePath);
- }
- if (!(await (0, dmgUtil_1.attachAndExecute)(tempDmg, true, () => customizeDmg(volumePath, specification, packager, backgroundFile)))) {
- return;
- }
- // dmg file must not exist otherwise hdiutil failed (https://github.com/electron-userland/electron-builder/issues/1308#issuecomment-282847594), so, -ov must be specified
- const args = ["convert", tempDmg, "-ov", "-format", specification.format, "-o", artifactPath];
- if (specification.format === "UDZO") {
- args.push("-imagekey", `zlib-level=${process.env.ELECTRON_BUILDER_COMPRESSION_LEVEL || "9"}`);
- }
- await (0, builder_util_1.spawn)("hdiutil", addLogLevel(args));
- if (this.options.internetEnabled && parseInt((0, os_1.release)().split(".")[0], 10) < 19) {
- await (0, builder_util_1.exec)("hdiutil", addLogLevel(["internet-enable"]).concat(artifactPath));
- }
- const licenseData = await (0, dmgLicense_1.addLicenseToDmg)(packager, artifactPath);
- if (packager.packagerOptions.effectiveOptionComputed != null) {
- await packager.packagerOptions.effectiveOptionComputed({ licenseData });
- }
- if (this.options.sign === true) {
- await this.signDmg(artifactPath);
- }
- const safeArtifactName = packager.computeSafeArtifactName(artifactName, "dmg");
- const updateInfo = this.options.writeUpdateInfo === false ? null : await (0, differentialUpdateInfoBuilder_1.createBlockmap)(artifactPath, this, packager, safeArtifactName);
- await packager.info.callArtifactBuildCompleted({
- file: artifactPath,
- safeArtifactName,
- target: this,
- arch,
- packager,
- isWriteUpdateInfo: updateInfo != null,
- updateInfo,
- });
- }
- async signDmg(artifactPath) {
- if (!(0, macCodeSign_1.isSignAllowed)(false)) {
- return;
- }
- const packager = this.packager;
- const qualifier = packager.platformSpecificBuildOptions.identity;
- // explicitly disabled if set to null
- if (qualifier === null) {
- // macPackager already somehow handle this situation, so, here just return
- return;
- }
- const keychainFile = (await packager.codeSigningInfo.value).keychainFile;
- const certificateType = "Developer ID Application";
- let identity = await (0, macCodeSign_1.findIdentity)(certificateType, qualifier, keychainFile);
- if (identity == null) {
- identity = await (0, macCodeSign_1.findIdentity)("Mac Developer", qualifier, keychainFile);
- if (identity == null) {
- return;
- }
- }
- const args = ["--sign", identity.hash];
- if (keychainFile != null) {
- args.push("--keychain", keychainFile);
- }
- args.push(artifactPath);
- await (0, builder_util_1.exec)("codesign", args);
- }
- computeVolumeName(arch, custom) {
- const appInfo = this.packager.appInfo;
- const shortVersion = this.packager.platformSpecificBuildOptions.bundleShortVersion || appInfo.version;
- const archString = (0, builder_util_1.getArchSuffix)(arch, this.packager.platformSpecificBuildOptions.defaultArch);
- if (custom == null) {
- return `${appInfo.productFilename} ${shortVersion}${archString}`;
- }
- return custom
- .replace(/\${arch}/g, archString)
- .replace(/\${shortVersion}/g, shortVersion)
- .replace(/\${version}/g, appInfo.version)
- .replace(/\${name}/g, appInfo.name)
- .replace(/\${productName}/g, appInfo.productName);
- }
- // public to test
- async computeDmgOptions() {
- const packager = this.packager;
- const specification = { ...this.options };
- if (specification.icon == null && specification.icon !== null) {
- specification.icon = await packager.getIconPath();
- }
- if (specification.icon != null && (0, builder_util_1.isEmptyOrSpaces)(specification.icon)) {
- throw new builder_util_1.InvalidConfigurationError("dmg.icon cannot be specified as empty string");
- }
- const background = specification.background;
- if (specification.backgroundColor != null) {
- if (background != null) {
- throw new builder_util_1.InvalidConfigurationError("Both dmg.backgroundColor and dmg.background are specified — please set the only one");
- }
- }
- else if (background == null) {
- specification.background = await (0, dmgUtil_1.computeBackground)(packager);
- }
- else {
- specification.background = path.resolve(packager.info.projectDir, background);
- }
- if (specification.format == null) {
- if (process.env.ELECTRON_BUILDER_COMPRESSION_LEVEL != null) {
- ;
- specification.format = "UDZO";
- }
- else if (packager.compression === "store") {
- specification.format = "UDRO";
- }
- else {
- specification.format = packager.compression === "maximum" ? "UDBZ" : "UDZO";
- }
- }
- if (specification.contents == null) {
- specification.contents = [
- {
- x: 130,
- y: 220,
- },
- {
- x: 410,
- y: 220,
- type: "link",
- path: "/Applications",
- },
- ];
- }
- return specification;
- }
- }
- exports.DmgTarget = DmgTarget;
- async function createStageDmg(tempDmg, appPath, volumeName) {
- //noinspection SpellCheckingInspection
- const imageArgs = addLogLevel(["create", "-srcfolder", appPath, "-volname", volumeName, "-anyowners", "-nospotlight", "-format", "UDRW"]);
- if (builder_util_1.log.isDebugEnabled) {
- imageArgs.push("-debug");
- }
- let filesystem = ["HFS+", "-fsargs", "-c c=64,a=16,e=16"];
- if (process.arch === "arm64") {
- // Apple Silicon `hdiutil` dropped support for HFS+, so we force the latest type
- // https://github.com/electron-userland/electron-builder/issues/4606
- filesystem = ["APFS"];
- builder_util_1.log.warn(null, "Detected arm64 process, HFS+ is unavailable. Creating dmg with APFS - supports Mac OSX 10.12+");
- }
- imageArgs.push("-fs", ...filesystem);
- imageArgs.push(tempDmg);
- // The reason for retrying up to ten times is that hdiutil create in some cases fail to unmount due to "resource busy".
- // https://github.com/electron-userland/electron-builder/issues/5431
- await (0, builder_util_1.retry)(() => (0, builder_util_1.spawn)("hdiutil", imageArgs), 5, 1000);
- return tempDmg;
- }
- function addLogLevel(args) {
- args.push(process.env.DEBUG_DMG === "true" ? "-verbose" : "-quiet");
- return args;
- }
- async function computeAssetSize(cancellationToken, dmgFile, specification, backgroundFile) {
- const asyncTaskManager = new builder_util_1.AsyncTaskManager(cancellationToken);
- asyncTaskManager.addTask((0, fs_extra_1.stat)(dmgFile));
- if (specification.icon != null) {
- asyncTaskManager.addTask((0, fs_1.statOrNull)(specification.icon));
- }
- if (backgroundFile != null) {
- asyncTaskManager.addTask((0, fs_extra_1.stat)(backgroundFile));
- }
- let result = 32 * 1024;
- for (const stat of await asyncTaskManager.awaitTasks()) {
- if (stat != null) {
- result += stat.size;
- }
- }
- return result;
- }
- async function customizeDmg(volumePath, specification, packager, backgroundFile) {
- const window = specification.window;
- const env = {
- ...process.env,
- volumePath,
- appFileName: `${packager.appInfo.productFilename}.app`,
- iconSize: specification.iconSize || 80,
- iconTextSize: specification.iconTextSize || 12,
- PYTHONIOENCODING: "utf8",
- };
- if (specification.backgroundColor != null || specification.background == null) {
- env.backgroundColor = specification.backgroundColor || "#ffffff";
- if (window != null) {
- env.windowX = (window.x == null ? 100 : window.x).toString();
- env.windowY = (window.y == null ? 400 : window.y).toString();
- env.windowWidth = (window.width || 540).toString();
- env.windowHeight = (window.height || 380).toString();
- }
- }
- else {
- delete env.backgroundColor;
- }
- const args = ["dmg", "--volume", volumePath];
- if (specification.icon != null) {
- args.push("--icon", (await packager.getResource(specification.icon)));
- }
- if (backgroundFile != null) {
- args.push("--background", backgroundFile);
- }
- const data = await (0, appBuilder_1.executeAppBuilderAsJson)(args);
- if (data.backgroundWidth != null) {
- env.windowWidth = window == null ? null : window.width;
- env.windowHeight = window == null ? null : window.height;
- if (env.windowWidth == null) {
- env.windowWidth = data.backgroundWidth.toString();
- }
- if (env.windowHeight == null) {
- env.windowHeight = data.backgroundHeight.toString();
- }
- if (env.windowX == null) {
- env.windowX = 400;
- }
- if (env.windowY == null) {
- env.windowY = Math.round((1440 - env.windowHeight) / 2).toString();
- }
- }
- Object.assign(env, data);
- const asyncTaskManager = new builder_util_1.AsyncTaskManager(packager.info.cancellationToken);
- env.iconLocations = await computeDmgEntries(specification, volumePath, packager, asyncTaskManager);
- await asyncTaskManager.awaitTasks();
- const executePython = async (execName) => {
- let pythonPath = process.env.PYTHON_PATH;
- if (!pythonPath) {
- pythonPath = (await (0, builder_util_1.exec)("which", [execName])).trim();
- }
- await (0, builder_util_1.exec)(pythonPath, [path.join((0, dmgUtil_1.getDmgVendorPath)(), "dmgbuild/core.py")], {
- cwd: (0, dmgUtil_1.getDmgVendorPath)(),
- env,
- });
- };
- try {
- await executePython("python3");
- }
- catch (error) {
- await executePython("python");
- }
- return packager.packagerOptions.effectiveOptionComputed == null || !(await packager.packagerOptions.effectiveOptionComputed({ volumePath, specification, packager }));
- }
- async function computeDmgEntries(specification, volumePath, packager, asyncTaskManager) {
- let result = "";
- for (const c of specification.contents) {
- if (c.path != null && c.path.endsWith(".app") && c.type !== "link") {
- builder_util_1.log.warn({ path: c.path, reason: "actual path to app will be used instead" }, "do not specify path for application");
- }
- const entryPath = c.path || `${packager.appInfo.productFilename}.app`;
- const entryName = c.name || path.basename(entryPath);
- const escapedEntryName = entryName.replace(/['\\]/g, match => `\\${match}`);
- if (result.length !== 0) {
- result += ",\n";
- }
- result += `'${escapedEntryName}': (${c.x}, ${c.y})`;
- if (c.type === "link") {
- asyncTaskManager.addTask((0, builder_util_1.exec)("ln", ["-s", `/${entryPath.startsWith("/") ? entryPath.substring(1) : entryPath}`, `${volumePath}/${entryName}`]));
- }
- // use c.path instead of entryPath (to be sure that this logic is not applied to .app bundle) https://github.com/electron-userland/electron-builder/issues/2147
- else if (!(0, builder_util_1.isEmptyOrSpaces)(c.path) && (c.type === "file" || c.type === "dir")) {
- const source = await packager.getResource(c.path);
- if (source == null) {
- builder_util_1.log.warn({ entryPath, reason: "doesn't exist" }, "skipped DMG item copying");
- continue;
- }
- const destination = `${volumePath}/${entryName}`;
- asyncTaskManager.addTask(c.type === "dir" || (await (0, fs_extra_1.stat)(source)).isDirectory() ? (0, fs_1.copyDir)(source, destination) : (0, fs_1.copyFile)(source, destination));
- }
- }
- return result;
- }
- async function transformBackgroundFileIfNeed(file, tmpDir) {
- if (file.endsWith(".tiff") || file.endsWith(".TIFF")) {
- return file;
- }
- const retinaFile = file.replace(/\.([a-z]+)$/, "@2x.$1");
- if (await (0, fs_1.exists)(retinaFile)) {
- const tiffFile = await tmpDir.getTempFile({ suffix: ".tiff" });
- await (0, builder_util_1.exec)("tiffutil", ["-cathidpicheck", file, retinaFile, "-out", tiffFile]);
- return tiffFile;
- }
- return file;
- }
- //# sourceMappingURL=dmg.js.map
|