FlatpakTarget.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const flatpak_bundler_1 = require("@malept/flatpak-bundler");
  4. const builder_util_1 = require("builder-util");
  5. const fs_extra_1 = require("fs-extra");
  6. const path = require("path");
  7. const core_1 = require("../core");
  8. const license_1 = require("../util/license");
  9. const targetUtil_1 = require("./targetUtil");
  10. class FlatpakTarget extends core_1.Target {
  11. constructor(name, packager, helper, outDir) {
  12. super(name);
  13. this.packager = packager;
  14. this.helper = helper;
  15. this.outDir = outDir;
  16. this.options = {
  17. ...this.packager.platformSpecificBuildOptions,
  18. ...this.packager.config[this.name],
  19. };
  20. }
  21. get appId() {
  22. return filterFlatpakAppIdentifier(this.packager.appInfo.id);
  23. }
  24. async build(appOutDir, arch) {
  25. const { packager, options } = this;
  26. const artifactName = packager.expandArtifactNamePattern(options, "flatpak", arch, undefined, false);
  27. const artifactPath = path.join(this.outDir, artifactName);
  28. await packager.info.callArtifactBuildStarted({
  29. targetPresentableName: "flatpak",
  30. file: artifactPath,
  31. arch,
  32. });
  33. const stageDir = await this.prepareStageDir(arch);
  34. const { manifest, buildOptions } = this.getFlatpakBuilderOptions(appOutDir, stageDir.dir, artifactName, arch);
  35. await (0, flatpak_bundler_1.bundle)(manifest, buildOptions);
  36. await stageDir.cleanup();
  37. await packager.info.callArtifactBuildCompleted({
  38. file: artifactPath,
  39. safeArtifactName: packager.computeSafeArtifactName(artifactName, "flatpak", arch, false),
  40. target: this,
  41. arch,
  42. packager,
  43. isWriteUpdateInfo: false,
  44. });
  45. }
  46. async prepareStageDir(arch) {
  47. const stageDir = await (0, targetUtil_1.createStageDir)(this, this.packager, arch);
  48. await Promise.all([this.createSandboxBinWrapper(stageDir), this.createDesktopFile(stageDir), this.copyLicenseFile(stageDir), this.copyIcons(stageDir)]);
  49. return stageDir;
  50. }
  51. async createSandboxBinWrapper(stageDir) {
  52. const useWaylandFlags = !!this.options.useWaylandFlags;
  53. const electronWrapperPath = stageDir.getTempFile(path.join("bin", "electron-wrapper"));
  54. await (0, fs_extra_1.outputFile)(electronWrapperPath, getElectronWrapperScript(this.packager.executableName, useWaylandFlags));
  55. await (0, fs_extra_1.chmod)(electronWrapperPath, 0o755);
  56. }
  57. async createDesktopFile(stageDir) {
  58. const appIdentifier = this.appId;
  59. const desktopFile = stageDir.getTempFile(path.join("share", "applications", `${appIdentifier}.desktop`));
  60. await this.helper.writeDesktopEntry(this.options, "electron-wrapper %U", desktopFile, { Icon: appIdentifier });
  61. }
  62. async copyLicenseFile(stageDir) {
  63. const licenseSrc = await (0, license_1.getNotLocalizedLicenseFile)(this.options.license, this.packager, ["txt", "html"]);
  64. if (licenseSrc) {
  65. const licenseDst = stageDir.getTempFile(path.join("share", "doc", this.appId, "copyright"));
  66. await (0, builder_util_1.copyFile)(licenseSrc, licenseDst);
  67. }
  68. }
  69. async copyIcons(stageDir) {
  70. const icons = await this.helper.icons;
  71. const copyIcons = icons.map(async (icon) => {
  72. const extWithDot = path.extname(icon.file);
  73. const sizeName = extWithDot === ".svg" ? "scalable" : `${icon.size}x${icon.size}`;
  74. const iconDst = stageDir.getTempFile(path.join("share", "icons", "hicolor", sizeName, "apps", `${this.appId}${extWithDot}`));
  75. return (0, builder_util_1.copyFile)(icon.file, iconDst);
  76. });
  77. await Promise.all(copyIcons);
  78. }
  79. getFlatpakBuilderOptions(appOutDir, stageDir, artifactName, arch) {
  80. const appIdentifier = this.appId;
  81. const { executableName } = this.packager;
  82. const flatpakArch = (0, builder_util_1.toLinuxArchString)(arch, "flatpak");
  83. const manifest = {
  84. id: appIdentifier,
  85. command: "electron-wrapper",
  86. runtime: this.options.runtime || flatpakBuilderDefaults.runtime,
  87. runtimeVersion: this.options.runtimeVersion || flatpakBuilderDefaults.runtimeVersion,
  88. sdk: this.options.sdk || flatpakBuilderDefaults.sdk,
  89. base: this.options.base || flatpakBuilderDefaults.base,
  90. baseVersion: this.options.baseVersion || flatpakBuilderDefaults.baseVersion,
  91. finishArgs: this.options.finishArgs || flatpakBuilderDefaults.finishArgs,
  92. branch: this.options.branch,
  93. modules: this.options.modules,
  94. };
  95. const buildOptions = {
  96. baseFlatpakref: `app/${manifest.base}/${flatpakArch}/${manifest.baseVersion}`,
  97. runtimeFlatpakref: `runtime/${manifest.runtime}/${flatpakArch}/${manifest.runtimeVersion}`,
  98. sdkFlatpakref: `runtime/${manifest.sdk}/${flatpakArch}/${manifest.runtimeVersion}`,
  99. arch: flatpakArch,
  100. bundlePath: path.join(this.outDir, artifactName),
  101. files: [[stageDir, "/"], [appOutDir, path.join("/lib", appIdentifier)], ...(this.options.files || [])],
  102. symlinks: [[path.join("/lib", appIdentifier, executableName), path.join("/bin", executableName)], ...(this.options.symlinks || [])],
  103. };
  104. return { manifest, buildOptions };
  105. }
  106. }
  107. exports.default = FlatpakTarget;
  108. const flatpakBuilderDefaults = {
  109. runtime: "org.freedesktop.Platform",
  110. runtimeVersion: "20.08",
  111. sdk: "org.freedesktop.Sdk",
  112. base: "org.electronjs.Electron2.BaseApp",
  113. baseVersion: "20.08",
  114. finishArgs: [
  115. // Wayland/X11 Rendering
  116. "--socket=wayland",
  117. "--socket=x11",
  118. "--share=ipc",
  119. // Open GL
  120. "--device=dri",
  121. // Audio output
  122. "--socket=pulseaudio",
  123. // Read/write home directory access
  124. "--filesystem=home",
  125. // Allow communication with network
  126. "--share=network",
  127. // System notifications with libnotify
  128. "--talk-name=org.freedesktop.Notifications",
  129. ],
  130. };
  131. function getElectronWrapperScript(executableName, useWaylandFlags) {
  132. return useWaylandFlags
  133. ? `#!/bin/sh
  134. export TMPDIR="$XDG_RUNTIME_DIR/app/$FLATPAK_ID"
  135. if [ "\${XDG_SESSION_TYPE}" == "wayland" ]; then
  136. zypak-wrapper "${executableName}" --enable-features=UseOzonePlatform --ozone-platform=wayland "$@"
  137. else
  138. zypak-wrapper "${executableName}" "$@"
  139. fi
  140. `
  141. : `#!/bin/sh
  142. export TMPDIR="$XDG_RUNTIME_DIR/app/$FLATPAK_ID"
  143. zypak-wrapper "${executableName}" "$@"
  144. `;
  145. }
  146. function filterFlatpakAppIdentifier(identifier) {
  147. // Remove special characters and allow only alphanumeric (A-Z,a-z,0-9), underscore (_), and period (.)
  148. // Flatpak documentation: https://docs.flatpak.org/en/latest/conventions.html#application-ids
  149. return identifier.replace(/-/g, "_").replace(/[^a-zA-Z0-9._]/g, "");
  150. }
  151. //# sourceMappingURL=FlatpakTarget.js.map