揭秘 Electron 的 Linux 打包过程:你知道背后发生了什么吗?

简介: 本文详细介绍了 `electron-builder` 在 Linux 平台上如何打包 Electron 应用程序,涵盖了 AppImage、Flatpak、Snap 等多种格式的打包原理和具体实现。文章从初始化 `LinuxPackager` 到创建各种目标格式的包,详细解析了每个步骤的代码逻辑和关键方法,帮助开发者更好地理解和使用 `electron-builder` 进行 Linux 应用的打包。

本文首发微信公众号:前端徐徐。

大家好,我是徐徐。今天我们聊聊 electron-builder 中 Linux 是如何打包的。

前言

electron-builder 是一个强大的工具,用于将 Electron 应用程序打包成可分发的格式。它支持多种平台,包括 Windows、macOS 和 Linux。在 Linux 平台上,electron-builder 支持多种打包格式,如 AppImage、Flatpak、Snap 等。本文将详细介绍 electron-builder 在 Linux 上的打包原理及各格式是如何打包的。

涉及的核心源码路径

  • linuxPackager.ts:Linux 平台 打包核心文件

https://github1s.com/electron-userland/electron-builder/blob/master/packages/app-builder-lib/src/linuxPackager.ts

  • AppImageTarget.ts:构建 AppImage 包

https://github1s.com/electron-userland/electron-builder/blob/master/packages/app-builder-lib/src/targets/AppImageTarget.ts

  • FlatpakTarget.ts:构建 Flatpak 包

https://github1s.com/electron-userland/electron-builder/blob/master/packages/app-builder-lib/src/targets/FlatpakTarget.ts

  • FpmTarget.ts:构建 deb, rpm, sh, freebsd, pacman, apk, p5p

https://github1s.com/electron-userland/electron-builder/blob/master/packages/app-builder-lib/src/targets/FpmTarget.ts

  • snap.ts:构建 Snap 包

https://github1s.com/electron-userland/electron-builder/blob/master/packages/app-builder-lib/src/targets/snap.ts

Linux 平台打包核心流程

初始化 LinuxPackager

export class LinuxPackager extends PlatformPackager<LinuxConfiguration> {
  readonly executableName: string
  constructor(info: Packager) {
    super(info, Platform.LINUX)
    const executableName = this.platformSpecificBuildOptions.executableName ?? info.config.executableName
    this.executableName = executableName == null ? this.appInfo.sanitizedName.toLowerCase() : sanitizeFileName(executableName)
  }
  // ...
}

首先,创建 LinuxPackager 类的实例。这个类继承自 PlatformPackager,专门用于处理 Linux 平台的打包。在构造函数中,它会设置可执行文件名称和其他 Linux 特定的配置。

确定打包目标

get defaultTarget(): Array<string> {
  return ["snap", "appimage"]
}

LinuxPackager 的 defaultTarget 属性定义了默认的打包目标,通常是 ["snap", "appimage"]。但实际使用的目标可能会根据用户的配置而有所不同。

创建打包目标

createTargets(targets: Array<string>, mapper: (name: string, factory: (outDir: string) => Target) => void): void {
  let helper: LinuxTargetHelper | null
  const getHelper = () => {
    if (helper == null) {
      helper = new LinuxTargetHelper(this)
    }
    return helper
  }
  for (const name of targets) {
    if (name === DIR_TARGET) {
      continue
    }
    const targetClass: typeof AppImageTarget | typeof SnapTarget | typeof FlatpakTarget | typeof FpmTarget | null = (() => {
      switch (name) {
        case "appimage":
          return require("./targets/AppImageTarget").default
        case "snap":
          return require("./targets/snap").default
        case "flatpak":
          return require("./targets/FlatpakTarget").default
        case "deb":
        case "rpm":
        case "sh":
        case "freebsd":
        case "pacman":
        case "apk":
        case "p5p":
          return require("./targets/FpmTarget").default
        default:
          return null
      }
    })()
    mapper(name, outDir => {
      if (targetClass === null) {
        return createCommonTarget(name, outDir, this)
      }
      return new targetClass(name, this, getHelper(), outDir)
    })
  }
}

核心方法是 createTargets,它会遍历所有指定的目标格式,并为每个格式创建相应的 Target 实例:

  • AppImage: 使用 AppImageTarget
  • Snap: 使用 SnapTarget
  • Flatpak: 使用 FlatpakTarget
  • deb, rpm, sh, freebsd, pacman, apk, p5p: 使用 FpmTarget
  • 其他格式: 使用 createCommonTarget

执行打包过程

对于每个目标,大致流程如下:

a. 准备工作:

  • 创建输出目录
  • 复制应用程序文件
  • 生成必要的元数据文件(如 .desktop 文件)

b. 格式特定的打包步骤:

  • AppImage: 使用 appimage-builder 创建 AppImage 文件
  • Snap: 生成 snapcraft.yaml 并使用 snapcraft 构建 snap 包
  • Flatpak: 创建必要的 manifest 文件并使用 flatpak-builder 构建 Flatpak 包
  • Fpm (deb, rpm 等): 使用 fpm 工具构建相应的包格式

c. 后处理:

  • 签名(如果配置了的话)
  • 移动生成的文件到最终输出目录

架构适配

export function toAppImageOrSnapArch(arch: Arch): string {
  switch (arch) {
    case Arch.x64:
      return "x86_64"
    case Arch.ia32:
      return "i386"
    case Arch.armv7l:
      return "arm"
    case Arch.arm64:
      return "arm_aarch64"
    default:
      throw new Error(`Unsupported arch ${arch}`)
  }
}

使用 toAppImageOrSnapArch 函数将 Electron 的架构名称转换为 AppImage 或 Snap 使用的架构名称。

清理

完成所有目标的打包后,清理临时文件和目录。

这就是 electron-builder 在 Linux 平台上打包的核心流程。每种特定的打包格式(如 AppImage、Snap、Flatpak 等)都有其独特的实现细节,但它们都遵循这个总体流程,下面我们来具体看看各种格式打包的具体实现。

创建 AppImage 包

初始化

export default class AppImageTarget extends Target {
  readonly options: AppImageOptions = { ...this.packager.platformSpecificBuildOptions, ...(this.packager.config as any)[this.name] }
  private readonly desktopEntry: Lazy<string>
  constructor(
    ignored: string,
    private readonly packager: LinuxPackager,
    private readonly helper: LinuxTargetHelper,
    readonly outDir: string
  ) {
    super("appImage")
    this.desktopEntry = new Lazy<string>(() => {
      const args = this.options.executableArgs?.join(" ") || "--no-sandbox"
      return helper.computeDesktopEntry(this.options, `AppRun ${args} %U`, {
        "X-AppImage-Version": `${packager.appInfo.buildVersion}`,
      })
    })
  }
  // ...
}
  • AppImageTarget 类继承自 Target,专门用于处理 AppImage 格式的打包。
  • 构造函数接收 LinuxPackager 和 LinuxTargetHelper 实例,这些提供了打包过程中的必要工具和方法。
  • 初始化配置选项,合并平台特定选项和通用选项。
  • 使用 Lazy 延迟计算桌面入口文件内容,优化性能。

构建过程(build 方法)

async build(appOutDir: string, arch: Arch): Promise<any> {
  // 准备工作
  const artifactName = packager.expandArtifactNamePattern(options, "AppImage", arch)
  const artifactPath = path.join(this.outDir, artifactName)
  await packager.info.callArtifactBuildStarted({
    targetPresentableName: "AppImage",
    file: artifactPath,
    arch,
  })
  // 并行处理多个准备任务
  const c = await Promise.all([
    this.desktopEntry.value,
    this.helper.icons,
    getAppUpdatePublishConfiguration(packager, arch, false),
    getNotLocalizedLicenseFile(options.license, this.packager, ["txt", "html"]),
    createStageDir(this, packager, arch),
  ])
  // 处理发布配置
  const publishConfig = c[2]
  if (publishConfig != null) {
    await outputFile(path.join(packager.getResourcesDir(stageDir.dir), "app-update.yml"), serializeToYaml(publishConfig))
  }
  // 构建 AppImage
  const args = [
    "appimage",
    "--stage", stageDir.dir,
    "--arch", Arch[arch],
    "--output", artifactPath,
    "--app", appOutDir,
    "--configuration", JSON.stringify({
      productName: this.packager.appInfo.productName,
      productFilename: this.packager.appInfo.productFilename,
      desktopEntry: c[0],
      executableName: this.packager.executableName,
      icons: c[1],
      fileAssociations: this.packager.fileAssociations,
      ...options,
    }),
  ]
  // 执行构建
  await packager.info.callArtifactBuildCompleted({
    file: artifactPath,
    safeArtifactName: packager.computeSafeArtifactName(artifactName, "AppImage", arch, false),
    target: this,
    arch,
    packager,
    isWriteUpdateInfo: true,
    updateInfo: await executeAppBuilderAsJson(args),
  })
}

a. 准备工作

b. 并行处理多个准备任务

c. 处理发布配置

d. 构建 AppImage

e. 执行构建

  • 确定输出文件名和路径。
  • 调用 artifactBuildStarted 回调,通知构建开始。
  • 获取桌面入口文件内容
  • 获取应用图标
  • 获取应用更新发布配置
  • 获取许可证文件
  • 创建临时工作目录(stage dir)
  • 如果存在发布配置,生成 app-update.yml 文件并保存到资源目录。
  • 准备 appimage 命令的参数,包括:
  • 临时目录路径
  • 目标架构
  • 输出路径
  • 应用目录
  • JSON 格式的配置信息(包含产品名称、文件名、桌面入口、可执行文件名、图标、文件关联等)
  • 如果指定了最大压缩级别,添加 xz 压缩参数。
  • 使用 executeAppBuilderAsJson 执行 appimage 命令。
  • 构建完成后调用 artifactBuildCompleted 回调,传递构建结果和更新信息。

错误处理和日志

await packager.info.callArtifactBuildStarted({
  targetPresentableName: "AppImage",
  file: artifactPath,
  arch,
})
// ... 构建过程 ...
await packager.info.callArtifactBuildCompleted({
  file: artifactPath,
  safeArtifactName: packager.computeSafeArtifactName(artifactName, "AppImage", arch, false),
  target: this,
  arch,
  packager,
  isWriteUpdateInfo: true,
  updateInfo: await executeAppBuilderAsJson(args),
})
  • 使用 Promise 和 async/await 处理异步操作。
  • 通过回调函数(artifactBuildStarted 和 artifactBuildCompleted)通知构建状态,便于外部监控和日志记录。

特殊功能支持

const args = [
  // ...
  "--configuration", JSON.stringify({
    productName: this.packager.appInfo.productName,
    productFilename: this.packager.appInfo.productFilename,
    desktopEntry: c[0],
    executableName: this.packager.executableName,
    icons: c[1],
    fileAssociations: this.packager.fileAssociations,
    ...options,
  }),
]
  • 支持自定义图标
  • 处理文件关联
  • 集成许可证文件
  • 支持应用自动更新配置

配置灵活性

readonly options: AppImageOptions = { ...this.packager.platformSpecificBuildOptions, ...(this.packager.config as any)[this.name] }
  • 通过 options 对象支持多种自定义选项
  • 支持从 package.json 或构建配置中读取选项

性能优化

this.desktopEntry = new Lazy<string>(() => {
  // ...
})
const c = await Promise.all([
  // ...
])
  • 使用 Lazy 延迟计算桌面入口文件内容
  • 利用 Promise.all 并行处理多个准备任务

总的来说,AppImageTarget 类封装了创建 AppImage 的完整流程,从准备必要的文件和配置,到执行构建命令,再到处理输出结果。它提供了高度的灵活性和可定制性,同时也优化了性能和资源使用。

创建 Flatpak 包

初始化 FlatpakTarget

constructor(
  name: string,
  private readonly packager: LinuxPackager,
  private helper: LinuxTargetHelper,
  readonly outDir: string
) {
  super(name)
}

FlatpakTarget 类继承自 Target,构造函数接收 LinuxPackager 和 LinuxTargetHelper 实例,这些提供了打包过程中的必要工具和方法。

构建过程

async build(appOutDir: string, arch: Arch): Promise<any> {
  // 准备工作
  const artifactName = packager.expandArtifactNamePattern(options, "flatpak", arch, undefined, false)
  const artifactPath = path.join(this.outDir, artifactName)
  
  // 通知构建开始
  await packager.info.callArtifactBuildStarted({
    targetPresentableName: "flatpak",
    file: artifactPath,
    arch,
  })
  // 准备临时目录
  const stageDir = await this.prepareStageDir(arch)
  // 获取 Flatpak 构建选项并执行构建
  const { manifest, buildOptions } = this.getFlatpakBuilderOptions(appOutDir, stageDir.dir, artifactName, arch)
  await bundleFlatpak(manifest, buildOptions)
  // 清理临时目录
  await stageDir.cleanup()
  // 通知构建完成
  await packager.info.callArtifactBuildCompleted({
    file: artifactPath,
    safeArtifactName: packager.computeSafeArtifactName(artifactName, "flatpak", arch, false),
    target: this,
    arch,
    packager,
    isWriteUpdateInfo: false,
  })
}

这个方法展示了 Flatpak 打包的整个流程,包括准备工作、构建和清理。

准备临时目录

private async prepareStageDir(arch: Arch): Promise<StageDir> {
  const stageDir = await createStageDir(this, this.packager, arch)
  await Promise.all([
    this.createSandboxBinWrapper(stageDir),
    this.createDesktopFile(stageDir),
    this.copyLicenseFile(stageDir),
    this.copyIcons(stageDir)
  ])
  return stageDir
}

这个方法准备了 Flatpak 打包所需的临时目录,包括创建沙箱包装器、桌面文件、复制许可证和图标等。

获取 Flatpak 构建选项

private getFlatpakBuilderOptions(appOutDir: string, stageDir: string, artifactName: string, arch: Arch): { manifest: FlatpakManifest; buildOptions: FlatpakBundlerBuildOptions } {
  // ... 省略部分代码 ...
  const manifest: FlatpakManifest = {
    id: appIdentifier,
    command: "electron-wrapper",
    runtime: this.options.runtime || flatpakBuilderDefaults.runtime,
    // ... 其他配置项 ...
  }
  const buildOptions: FlatpakBundlerBuildOptions = {
    baseFlatpakref: `app/${manifest.base}/${flatpakArch}/${manifest.baseVersion}`,
    // ... 其他构建选项 ...
  }
  return { manifest, buildOptions }
}

这个方法生成了 Flatpak 构建所需的配置和选项。

特殊功能支持

  • 沙箱包装器 (createSandboxBinWrapper 方法)
  • 桌面文件生成 (createDesktopFile 方法)
  • 许可证文件复制 (copyLicenseFile 方法)
  • 图标复制 (copyIcons 方法)

这些方法处理了 Flatpak 特有的一些需求,如沙箱环境、桌面集成等。

辅助函数

  • getElectronWrapperScript: 生成 Electron 包装器脚本
  • filterFlatpakAppIdentifier: 过滤应用标识符,确保符合 Flatpak 规范

整个 FlatpakTarget 类封装了创建 Flatpak 包的完整流程。它利用了 @malept/flatpak-bundler 库来执行实际的构建过程,同时处理了 Flatpak 特有的需求,如沙箱环境、桌面集成等。整个过程包括初始化、准备临时目录、生成必要的文件和配置、执行构建、清理等步骤。这个实现展示了如何将 Electron 应用适配到 Flatpak 格式,并集成到 electron-builder 的整体构建流程中。

创建 Fpm 包

初始化 FpmTarget

constructor(
  name: string,
  private readonly packager: LinuxPackager,
  private readonly helper: LinuxTargetHelper,
  readonly outDir: string
) {
  super(name, false)
  this.scriptFiles = this.createScripts()
}

FpmTarget 类继承自 Target,构造函数接收 LinuxPackager 和 LinuxTargetHelper 实例,并初始化脚本文件。

创建脚本文件

private async createScripts(): Promise<Array<string>> {
  // ... 省略部分代码 ...
  return await Promise.all<string>([
    writeConfigFile(packager.info.tempDirManager, getResource(this.options.afterInstall, "after-install.tpl"), templateOptions),
    writeConfigFile(packager.info.tempDirManager, getResource(this.options.afterRemove, "after-remove.tpl"), templateOptions),
  ])
}

这个方法创建了安装后和卸载后的脚本文件。

构建过程

async build(appOutDir: string, arch: Arch): Promise<any> {
  // ... 准备工作 ...
  const args = [
    "--architecture", toLinuxArchString(arch, target),
    "--after-install", scripts[0],
    "--after-remove", scripts[1],
    // ... 其他参数 ...
  ]
  // ... 设置其他选项 ...
  await executeAppBuilder(["fpm", "--configuration", JSON.stringify(fpmConfiguration)], undefined, { env })
  // ... 处理构建结果 ...
}

build 方法是打包的核心,它准备了 fpm 命令所需的参数,然后执行打包过程。

计算 fpm 元信息选项

private async computeFpmMetaInfoOptions(): Promise<FpmOptions> {
  // ... 省略部分代码 ...
  return {
    name: options.packageName ?? this.packager.appInfo.linuxPackageName,
    maintainer: author!,
    url: projectUrl!,
    vendor: options.vendor || author!,
  }
}

这个方法计算了 fpm 所需的元信息,如包名、维护者、URL 等。

自动更新支持

const publishConfig = this.supportsAutoUpdate(target)
  ? await getAppUpdatePublishConfiguration(packager, arch, false)
  : null
if (publishConfig != null) {
  // ... 添加自动更新文件 ...
}

对于支持自动更新的目标格式(如 deb、rpm、pacman),会添加相应的更新配置文件。

特殊处理

  • 针对不同目标格式(deb、rpm)的特殊处理
  • 处理依赖项、推荐包等
  • 处理图标、桌面文件、MIME 类型文件等

环境变量设置

const env = {
  ...process.env,
  SZA_PATH: await getPath7za(),
  SZA_COMPRESSION_LEVEL: packager.compression === "store" ? "0" : "9",
}

设置了特定的环境变量,用于控制压缩等行为。

执行 fpm 命令

await executeAppBuilder(["fpm", "--configuration", JSON.stringify(fpmConfiguration)], undefined, { env })

通过 executeAppBuilder 执行实际的 fpm 命令来创建包。

处理构建结果

await packager.info.callArtifactBuildCompleted(info)

构建完成后,调用回调函数通知构建结果。
FpmTarget 类封装了使用 fpm 工具创建各种 Linux 包格式(如 deb、rpm 等)的完整流程。它处理了从准备脚本文件、设置构建参数、执行构建命令到处理构建结果的整个过程。这个实现展示了如何将 Electron 应用适配到各种 Linux 包格式,并集成到 electron-builder 的整体构建流程中。特别值得注意的是,它还包含了对自动更新的支持,以及针对不同目标格式的特殊处理。

创建 snap 包

初始化 SnapTarget 类

export default class SnapTarget extends Target {
  readonly options: SnapOptions = { ...this.packager.platformSpecificBuildOptions, ...(this.packager.config as any)[this.name] }
  public isUseTemplateApp = false
  constructor(
    name: string,
    private readonly packager: LinuxPackager,
    private readonly helper: LinuxTargetHelper,
    readonly outDir: string
  ) {
    super(name)
  }

这里定义了 SnapTarget 类,继承自 Target。构造函数接收打包器、帮助器和输出目录等参数。options 属性合并了平台特定的构建选项和配置。

创建 Snap 包的描述符

private async createDescriptor(arch: Arch): Promise<any> {
  // 版本检查
  if (!this.isElectronVersionGreaterOrEqualThan("4.0.0")) {
    // ...版本警告和错误处理
  }
  // 设置基本信息
  const appInfo = this.packager.appInfo
  const snapName = this.packager.executableName.toLowerCase()
  
  // 处理插件和槽位
  const plugs = normalizePlugConfiguration(this.options.plugs)
  const plugNames = this.replaceDefault(plugs == null ? null : Object.getOwnPropertyNames(plugs), defaultPlugs)
  const slots = normalizePlugConfiguration(this.options.slots)
  // 处理包配置
  const buildPackages = asArray(options.buildPackages)
  const defaultStagePackages = getDefaultStagePackages()
  const stagePackages = this.replaceDefault(options.stagePackages, defaultStagePackages)
  // 创建应用描述符
  const appDescriptor: any = {
    command: "command.sh",
    plugs: plugNames,
    adapter: "none",
  }
  // 加载和修改 snap 配置
  const snap: any = load(await readFile(path.join(getTemplatePath("snap"), "snapcraft.yaml"), "utf-8"))
  // ... 更多配置处理
  // 返回完整的 snap 描述符
  return snap
}

这个方法负责创建 Snap 包的描述符,包括版本检查、基本信息设置、插件和槽位处理、包配置等。

构建

async build(appOutDir: string, arch: Arch): Promise<any> {
  // 准备构建参数
  const artifactName = packager.expandArtifactNamePattern(this.options, "snap", arch, "${name}_${version}_${arch}.${ext}", false)
  const artifactPath = path.join(this.outDir, artifactName)
  // 创建描述符
  const snap = await this.createDescriptor(arch)
  // 准备构建目录和参数
  const stageDir = await createStageDirPath(this, packager, arch)
  const snapArch = toLinuxArchString(arch, "snap")
  const args = ["snap", "--app", appOutDir, "--stage", stageDir, "--arch", snapArch, "--output", artifactPath, "--executable", this.packager.executableName]
  // 处理图标
  // ... 图标处理逻辑
  // 写入桌面入口文件
  await this.helper.writeDesktopEntry(/* ... */)
  // 处理额外的应用参数
  // ... 参数处理逻辑
  // 写入 snap 配置文件
  await outputFile(path.join(snapMetaDir, this.isUseTemplateApp ? "snap.yaml" : "snapcraft.yaml"), serializeToYaml(snap))
  // 执行构建
  await executeAppBuilder(args)
  // 处理发布配置
  const publishConfig = findSnapPublishConfig(this.packager.config)
  // 通知构建完成
  await packager.info.callArtifactBuildCompleted(/* ... */)
}

build 方法orchestrates整个构建过程,包括准备构建参数、创建描述符、处理图标和桌面入口、执行实际构建,以及处理构建完成后的操作。

辅助方法和函数

private replaceDefault(inList: Array<string> | null | undefined, defaultList: Array<string>) {
  const result = _replaceDefault(inList, defaultList)
  if (result !== defaultList) {
    this.isUseTemplateApp = false
  }
  return result
}
private isElectronVersionGreaterOrEqualThan(version: string) {
  return semver.gte(this.packager.config.electronVersion || "7.0.0", version)
}
function normalizePlugConfiguration(raw: Array<string | PlugDescriptor> | PlugDescriptor | null | undefined): { [key: string]: { [name: string]: any } | null } | null {
  // ... 插件配置规范化逻辑
}
function findSnapPublishConfig(config?: Configuration): SnapStoreOptions | null {
  // ... 查找 Snap 发布配置的逻辑
}

这些方法和函数提供了各种辅助功能,如替换默认值、版本比较、配置规范化等。

SnapTarget 类提供了一个全面的解决方案来构建 Snap 包。它处理了从配置解析到实际构建的所有方面,包括版本兼容性检查、配置合并、描述符生成、文件生成等。代码结构清晰,通过方法分离实现了关注点分离,使得整个构建过程更易于管理和扩展。

结语

通过以上的分析,我们深入了解了 electron-builder 在 Linux 平台上的打包流程。无论是 AppImage、Flatpak 还是 FPM 格式,每种包的创建都有其独特的步骤和注意事项。在实际应用中,开发者可以根据需要选择适合的打包格式,并利用 electron-builder 提供的灵活配置选项来定制打包过程。

希望这篇文章能够帮助你更好地理解和使用 electron-builder 进行 Linux 应用的打包。如果你有任何问题或想法,欢迎在评论区分享!

相关文章
|
2月前
|
资源调度 运维 JavaScript
使用electron创建桌面应用及常见打包错误解决
使用electron创建桌面应用及常见打包错误解决
267 3
|
4月前
|
Linux
在Linux中,列出几种常见打包工具并写相应解压缩参数。
在Linux中,列出几种常见打包工具并写相应解压缩参数。
|
6月前
|
资源调度 JavaScript 前端开发
IM跨平台技术学习(十一):环信基于Electron打包Web IM桌面端的技术实践
这次借着论证 Web IM端 SDK 是否可以在 Electron 生成的桌面端正常稳定使用,我决定把官方新推出的 webim-vue3-demo,打包到桌面端,并记录了这次验证的过程以及所遇到的问题和解决方法。
98 2
|
2月前
|
安全 前端开发 iOS开发
揭秘 electron-builder:macOS 应用打包背后到底发生了什么?
本文详细介绍了 Electron 应用在 macOS 平台上的打包流程,涵盖配置文件、打包步骤、签名及 notarization 等关键环节。通过剖析 `electron-builder` 的源码,展示了如何处理多架构应用、执行签名,并解决常见问题。适合希望深入了解 macOS 打包细节的开发者。
66 2
|
2月前
|
XML 缓存 前端开发
Electron-builder 是如何打包 Windows 应用的?
本文首发于微信公众号“前端徐徐”,作者徐徐深入解析了 electron-builder 在 Windows 平台上的打包流程。文章详细介绍了 `winPackager.ts`、`AppxTarget.ts`、`MsiTarget.ts` 和 `NsisTarget.ts` 等核心文件,涵盖了目标创建、图标处理、代码签名、资源编辑、应用签名、性能优化等内容,并分别讲解了 AppX/MSIX、MSI 和 NSIS 安装程序的生成过程。通过这些内容,读者可以更好地理解和使用 electron-builder 进行 Windows 应用的打包和发布。
157 0
|
4月前
|
Linux C# C++
【Azure App Service For Container】创建ASP.NET Core Blazor项目并打包为Linux镜像发布到Azure应用服务
【Azure App Service For Container】创建ASP.NET Core Blazor项目并打包为Linux镜像发布到Azure应用服务
|
4月前
|
Linux 数据安全/隐私保护 Python
LInux下 python混淆代码打包产出exe
安装 PyArmor 加密Python程序:使用`pip install pyarmor`。为避免混淆 venv 目录,可指定排除此目录:`.\/venv\/bin\/pyarmor-7 pack -e \"--onefile\" -x \"--exclude venv\" main.py`。查阅详细文档:[官方指南](https://pyarmor.readthedocs.io/zh/v7.x/advanced.html)。
|
4月前
|
iOS开发 MacOS Python
Electron Mac 打包报 Error: Exit code: ENOENT. spawn /usr/bin/python ENOENT 解决方法
Electron Mac 打包报 Error: Exit code: ENOENT. spawn /usr/bin/python ENOENT 解决方法
|
6月前
|
存储 Linux 程序员
tar命令详解:linux文件打包神器
tar命令详解:linux文件打包神器
|
5月前
|
JavaScript 网络安全 iOS开发
如何用 Electron 打包chatgpt-plus.top并生成mac客户端
如何用 Electron 打包chatgpt-plus.top并生成mac客户端
59 0