diff --git a/packages/app-builder-lib/package.json b/packages/app-builder-lib/package.json index c1e79ac69ba..ca5903c6fc9 100644 --- a/packages/app-builder-lib/package.json +++ b/packages/app-builder-lib/package.json @@ -48,7 +48,7 @@ "dependencies": { "@develar/schema-utils": "~2.6.5", "@electron/fuses": "^1.8.0", - "@electron/asar": "^3.2.13", + "@electron/asar": "3.2.13", "@electron/notarize": "2.5.0", "@electron/osx-sign": "1.3.1", "@electron/rebuild": "3.7.0", diff --git a/packages/app-builder-lib/src/asar/asarUtil.ts b/packages/app-builder-lib/src/asar/asarUtil.ts index ffde70952f2..790a8179a30 100644 --- a/packages/app-builder-lib/src/asar/asarUtil.ts +++ b/packages/app-builder-lib/src/asar/asarUtil.ts @@ -1,9 +1,10 @@ import { CreateOptions, createPackageWithOptions } from "@electron/asar" import { AsyncTaskManager, log } from "builder-util" import { CancellationToken } from "builder-util-runtime" -import { Filter, MAX_FILE_REQUESTS } from "builder-util/out/fs" -import * as fsNode from "fs" +import { FileCopier, Filter, Link, MAX_FILE_REQUESTS } from "builder-util/out/fs" import * as fs from "fs-extra" +import { mkdir, readlink, symlink } from "fs-extra" +import { platform } from "os" import * as path from "path" import * as tempFile from "temp-file" import { AsarOptions } from "../options/PlatformSpecificBuildOptions" @@ -15,9 +16,12 @@ import { detectUnpackedDirs } from "./unpackDetector" export class AsarPackager { private readonly outFile: string private rootForAppFilesWithoutAsar!: string - private readonly tmpDir = new tempFile.TmpDir() + private readonly fileCopier = new FileCopier() + private readonly tmpDir: tempFile.TmpDir + private readonly cancellationToken: CancellationToken constructor( + readonly packager: PlatformPackager, private readonly config: { defaultDestination: string resourcePath: string @@ -26,14 +30,13 @@ export class AsarPackager { } ) { this.outFile = path.join(config.resourcePath, `app.asar`) + this.tmpDir = packager.info.tempDirManager + this.cancellationToken = packager.info.cancellationToken } - async pack(fileSets: Array, _packager: PlatformPackager) { + async pack(fileSets: Array) { this.rootForAppFilesWithoutAsar = await this.tmpDir.getTempDir({ prefix: "asar-app" }) - const cancellationToken = new CancellationToken() - cancellationToken.on("cancel", () => this.tmpDir.cleanupSync()) - const orderedFileSets = [ // Write dependencies first to minimize offset changes to asar header ...fileSets.slice(1), @@ -42,9 +45,13 @@ export class AsarPackager { fileSets[0], ].map(orderFileSet) - const { unpackedPaths, copiedFiles } = await this.detectAndCopy(orderedFileSets, cancellationToken) + const { unpackedPaths, copiedFiles } = await this.detectAndCopy(orderedFileSets) const unpackGlob = unpackedPaths.length > 1 ? `{${unpackedPaths.join(",")}}` : unpackedPaths.pop() + await this.executeElectronAsar(copiedFiles, unpackGlob) + } + + private async executeElectronAsar(copiedFiles: string[], unpackGlob: string | undefined) { let ordering = this.config.options.ordering || undefined if (!ordering) { // `copiedFiles` are already ordered due to `orderedFileSets` input, so we just map to their relative paths (via substring) within the asar. @@ -69,15 +76,17 @@ export class AsarPackager { } await createPackageWithOptions(this.rootForAppFilesWithoutAsar, this.outFile, options) console.log = consoleLogger - - await this.tmpDir.cleanup() } - private async detectAndCopy(fileSets: ResolvedFileSet[], cancellationToken: CancellationToken) { - const taskManager = new AsyncTaskManager(cancellationToken) + private async detectAndCopy(fileSets: ResolvedFileSet[]) { + const taskManager = new AsyncTaskManager(this.cancellationToken) const unpackedPaths = new Set() const copiedFiles = new Set() + const createdSourceDirs = new Set() + const links: Array = [] + const symlinkType = platform() === "win32" ? "junction" : "file" + const matchUnpacker = (file: string, dest: string, stat: fs.Stats) => { if (this.config.unpackPattern?.(file, stat)) { log.debug({ file }, "unpacking") @@ -85,79 +94,88 @@ export class AsarPackager { return } } - const writeFileOrSymlink = async (options: { transformedData: string | Buffer | undefined; file: string; destination: string; stat: fs.Stats; fileSet: ResolvedFileSet }) => { - const { - transformedData, - file: source, - destination, - stat, - fileSet: { src: sourceDir }, - } = options + const writeFileOrProcessSymlink = async (options: { + file: string + destination: string + stat: fs.Stats + fileSet: ResolvedFileSet + transformedData: string | Buffer | undefined + }) => { + const { transformedData, file, destination, stat, fileSet } = options + if (!stat.isFile() && !stat.isSymbolicLink()) { + return + } copiedFiles.add(destination) - // If transformed data, skip symlink logic - if (transformedData) { - return this.copyFileOrData(transformedData, source, destination, stat) + const dir = path.dirname(destination) + if (!createdSourceDirs.has(dir)) { + await mkdir(dir, { recursive: true }) + createdSourceDirs.add(dir) } - const realPathFile = await fs.realpath(source) - - if (source === realPathFile) { - return this.copyFileOrData(undefined, source, destination, stat) + // write any data if provided, skip symlink check + if (transformedData != null) { + return fs.writeFile(destination, transformedData, { mode: stat.mode }) } - const realPathRelative = path.relative(sourceDir, realPathFile) + const realPathFile = await fs.realpath(file) + const realPathRelative = path.relative(fileSet.src, realPathFile) const isOutsidePackage = realPathRelative.startsWith("..") if (isOutsidePackage) { - log.error({ source: log.filePath(source), realPathFile: log.filePath(realPathFile) }, `unable to copy, file is symlinked outside the package`) - throw new Error( - `Cannot copy file (${path.basename(source)}) symlinked to file (${path.basename(realPathFile)}) outside the package as that violates asar security integrity` - ) + log.error({ source: log.filePath(file), realPathFile: log.filePath(realPathFile) }, `unable to copy, file is symlinked outside the package`) + throw new Error(`Cannot copy file (${path.basename(file)}) symlinked to file (${path.basename(realPathFile)}) outside the package as that violates asar security integrity`) } - const symlinkTarget = path.resolve(this.rootForAppFilesWithoutAsar, realPathRelative) - await this.copyFileOrData(undefined, source, symlinkTarget, stat) - const target = path.relative(path.dirname(destination), symlinkTarget) - fsNode.symlinkSync(target, destination) + // not a symlink, copy directly + if (file === realPathFile) { + return this.fileCopier.copy(file, destination, stat) + } - copiedFiles.add(symlinkTarget) + // okay, it must be a symlink. evaluate link to be relative to source file in asar + let link = await readlink(file) + if (path.isAbsolute(link)) { + link = path.relative(path.dirname(file), link) + } + links.push({ file: destination, link }) } for await (const fileSet of fileSets) { if (this.config.options.smartUnpack !== false) { detectUnpackedDirs(fileSet, unpackedPaths, this.config.defaultDestination) } + + // Don't use BluebirdPromise, we need to retain order of execution/iteration through the ordered fileset for (let i = 0; i < fileSet.files.length; i++) { const file = fileSet.files[i] const transformedData = fileSet.transformedFiles?.get(i) - const metadata = fileSet.metadata.get(file) || (await fs.lstat(file)) + const stat = fileSet.metadata.get(file)! const relative = path.relative(this.config.defaultDestination, getDestinationPath(file, fileSet)) - const dest = path.resolve(this.rootForAppFilesWithoutAsar, relative) + const destination = path.resolve(this.rootForAppFilesWithoutAsar, relative) - matchUnpacker(file, dest, metadata) - taskManager.addTask(writeFileOrSymlink({ transformedData, file, destination: dest, stat: metadata, fileSet })) + matchUnpacker(file, destination, stat) + taskManager.addTask(writeFileOrProcessSymlink({ transformedData, file, destination, stat, fileSet })) if (taskManager.tasks.length > MAX_FILE_REQUESTS) { await taskManager.awaitTasks() } } } + // finish copy then set up all symlinks + await taskManager.awaitTasks() + for (const it of links) { + taskManager.addTask(symlink(it.link, it.file, symlinkType)) + + if (taskManager.tasks.length > MAX_FILE_REQUESTS) { + await taskManager.awaitTasks() + } + } await taskManager.awaitTasks() return { unpackedPaths: Array.from(unpackedPaths), copiedFiles: Array.from(copiedFiles), } } - - private async copyFileOrData(data: string | Buffer | undefined, source: string, destination: string, stat: fs.Stats) { - await fs.mkdir(path.dirname(destination), { recursive: true }) - if (data) { - await fs.writeFile(destination, data, { mode: stat.mode }) - } else { - await fs.copyFile(source, destination) - } - } } function orderFileSet(fileSet: ResolvedFileSet): ResolvedFileSet { diff --git a/packages/app-builder-lib/src/platformPackager.ts b/packages/app-builder-lib/src/platformPackager.ts index 7edec89fc25..db7e17281be 100644 --- a/packages/app-builder-lib/src/platformPackager.ts +++ b/packages/app-builder-lib/src/platformPackager.ts @@ -504,12 +504,12 @@ export abstract class PlatformPackager await transformFiles(transformer, fileSet) } - await new AsarPackager({ + await new AsarPackager(this, { defaultDestination, resourcePath, options: asarOptions, unpackPattern: fileMatcher?.createFilter(), - }).pack(fileSets, this) + }).pack(fileSets) }) ) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index caf52373e6f..d76e81941c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,7 +84,7 @@ importers: specifier: ~2.6.5 version: 2.6.5 '@electron/asar': - specifier: ^3.2.13 + specifier: 3.2.13 version: 3.2.13 '@electron/fuses': specifier: ^1.8.0 @@ -8723,7 +8723,7 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 20.16.10 + '@types/node': 22.7.4 optional: true '@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1)(typescript@5.6.2))(eslint@9.11.1)(typescript@5.6.2)': diff --git a/test/fixtures/test-app-symlink-framework/hello-world/hello-world-framework.sh b/test/fixtures/test-app-symlink-framework/hello-world/hello-world-framework.sh new file mode 100644 index 00000000000..de4ff8d2587 --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/hello-world-framework.sh @@ -0,0 +1,222 @@ +#!/bin/bash + +set -ex + +# Ignore lines that start with a hash (#) +# setopt INTERACTIVE_COMMENTS + +# # Skip commands where the glob pattern does not match any files +# setopt null_glob + +# Prepare an empty folder +BUILD_DIR="$(pwd)/lib/Release/Hello" +rm -rf "${BUILD_DIR}" +mkdir -p "${BUILD_DIR}" +cd "${BUILD_DIR}" + +# BUILD DYNAMIC LIBRARY + +# Compile the library source +cat << EOF > Greeter.swift +public class Greeter { + public init() {} + public func hello() { + print("Hello World!") + } +} +EOF + +# Create directories for each architecture +mkdir -p arm64 x86_64 + +# Compile Hello.swift to object files and generate module files for each architecture +swiftc -parse-as-library \ + -emit-object -o arm64/Hello.o \ + -emit-module -module-name Hello -emit-module-path arm64/Hello.swiftmodule \ + -enable-library-evolution -emit-module-interface-path arm64/Hello.swiftinterface \ + -target arm64-apple-macosx10.9.0 \ + Greeter.swift + +swiftc -parse-as-library \ + -emit-object -o x86_64/Hello.o \ + -emit-module -module-name Hello -emit-module-path x86_64/Hello.swiftmodule \ + -enable-library-evolution -emit-module-interface-path x86_64/Hello.swiftinterface \ + -target x86_64-apple-macosx10.9.0 \ + Greeter.swift + +# Create a universal (fat) static library from the object files +lipo -create arm64/Hello.o x86_64/Hello.o -output Hello.o +libtool -static -o libHello.a Hello.o +rm Hello.o + + +# BUILD STATIC FRAMEWORK + +# Create framework structure +mkdir -p Hello.framework/Versions/A/ +mkdir -p Hello.framework/Versions/A/Modules +mkdir -p Hello.framework/Versions/A/Modules/Hello.swiftmodule +mkdir -p Hello.framework/Versions/A/Headers +mkdir -p Hello.framework/Versions/A/Resources + +# Move static library +mv libHello.a Hello.framework/Versions/A/Hello + +# Move module files +mv arm64/Hello.swiftdoc Hello.framework/Versions/A/Modules/ +mv arm64/Hello.abi.json Hello.framework/Versions/A/Modules/ +mv arm64/Hello.swiftsourceinfo Hello.framework/Versions/A/Modules/ + +mv arm64/Hello.swiftmodule Hello.framework/Versions/A/Modules/Hello.swiftmodule/arm64.swiftmodule +mv arm64/Hello.swiftinterface Hello.framework/Versions/A/Modules/Hello.swiftmodule/arm64.swiftinterface +mv arm64/Hello.private.swiftinterface Hello.framework/Versions/A/Modules/Hello.swiftmodule/arm64.private.swiftinterface + +mv x86_64/Hello.swiftmodule Hello.framework/Versions/A/Modules/Hello.swiftmodule/x86_64.swiftmodule +mv x86_64/Hello.swiftinterface Hello.framework/Versions/A/Modules/Hello.swiftmodule/x86_64.swiftinterface +mv x86_64/Hello.private.swiftinterface Hello.framework/Versions/A/Modules/Hello.swiftmodule/x86_64.private.swiftinterface + +# This modulemap is superfluous unless you use the library from Objetive-C. +cat << EOF > Hello.framework/Versions/A/Modules/module.modulemap +framework module Hello { + header "HelloFramework.h" + export * +} +EOF + +# This header is superfluous unless you use the library from Objetive-C. +cat << EOF > Hello.framework/Versions/A/Headers/HelloFramework.h +#import +void hello(void); +EOF + +cat << EOF > Hello.framework/Versions/A/Info.plist + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + Hello + CFBundleIdentifier + com.example.Hello + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Hello + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2024 Hello Company. All rights reserved. + + +EOF + +# Create symbolic links to speed up access +cd Hello.framework/Versions +ln -s A Current +cd .. +ln -s Versions/Current/Hello Hello +ln -s Versions/Current/Headers Headers +ln -s Versions/Current/Info.plist Info.plist +ln -s Versions/Current/Resources Resources +ln -s Versions/Current/Modules Modules + +cd .. +chmod -R 755 Hello.framework + + +# BUILD THE CLIENT + +# Create UseHello.swift +cat << EOF > UseHello.swift +import Hello +@main +public struct UseHello { + public static func main() { + let greeter = Greeter() + greeter.hello() + } +} +EOF + +# Compile UseHello.swift into executables for each architecture +swiftc -parse-as-library \ + -o UseHello-arm64 UseHello.swift \ + -target arm64-apple-macosx10.9.0 \ + -F. -framework Hello -I Hello.framework/Modules/arm64 + +swiftc -parse-as-library \ + -o UseHello-x86_64 UseHello.swift \ + -target x86_64-apple-macosx10.9.0 \ + -F. -framework Hello -I Hello.framework/Modules/x86_64 + +# Create a universal (fat) binary +lipo -create UseHello-arm64 UseHello-x86_64 -output UseHello + +# Clean up intermediate files +rm UseHello-arm64 UseHello-x86_64 + +# cleanup +rm -rf arm64 +rm -rf x86_64 + + +# EXECUTE + +# Execute +./UseHello + + +# CREATE EXECUTABLE PACKAGE + +# Encapsulate the libHello.a in a xcframework +xcodebuild -create-xcframework \ + -framework Hello.framework \ + -output Hello.xcframework + +# Optionally sign the framework +# security find-identity -v -p codesigning +# codesign --sign "YOUR_ID_HERE" --timestamp --options runtime Hello.xcframework + +# Remove everything except the xcframework +# I’m discarding the message 'rm: Hello.xcframework: is a directory' +rm * 2>/dev/null + +# Create an executable +swift package init --type executable --name UseHello + +# Overwrite the Package.swift to add the dependency +cat << EOF > Package.swift +// swift-tools-version: 6.0 +import PackageDescription + +let package = Package( + name: "UseHello", + platforms: [.macOS(.v15)], + products: [ + .executable(name: "UseHello", targets: ["UseHello"]) + ], + dependencies: [], + targets: [ + .executableTarget(name: "UseHello", dependencies: ["Hello"]), + .binaryTarget(name: "Hello", path: "./Hello.xcframework") + ] +) +EOF + +# Replace the default main.swift file. +rm Sources/main.swift +mkdir -p Sources/UseHello +cat << EOF > Sources/UseHello/main.swift +import Hello +Greeter().hello() +EOF + +swift run --arch x86_64 +swift run --arch arm64 +swift build --arch x86_64 --arch arm64 \ No newline at end of file diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Headers b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Headers new file mode 120000 index 00000000000..a177d2a6b92 --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Hello b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Hello new file mode 120000 index 00000000000..c1f0db26723 --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Hello @@ -0,0 +1 @@ +Versions/Current/Hello \ No newline at end of file diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Info.plist b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Info.plist new file mode 120000 index 00000000000..ec889aa3c19 --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Info.plist @@ -0,0 +1 @@ +Versions/Current/Info.plist \ No newline at end of file diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Modules b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Modules new file mode 120000 index 00000000000..5736f3186e7 --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Headers/HelloFramework.h b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Headers/HelloFramework.h new file mode 100755 index 00000000000..1af71a0b025 --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Headers/HelloFramework.h @@ -0,0 +1,2 @@ +#import +void hello(void); diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Hello b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Hello new file mode 100755 index 00000000000..02ae196e491 Binary files /dev/null and b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Hello differ diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Info.plist b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Info.plist new file mode 100755 index 00000000000..59375bf54f6 --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + Hello + CFBundleIdentifier + com.example.Hello + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Hello + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2024 Hello Company. All rights reserved. + + diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.abi.json b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.abi.json new file mode 100755 index 00000000000..54700d0a8a8 --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.abi.json @@ -0,0 +1,82 @@ +{ + "ABIRoot": { + "kind": "Root", + "name": "Hello", + "printedName": "Hello", + "children": [ + { + "kind": "TypeDecl", + "name": "Greeter", + "printedName": "Greeter", + "children": [ + { + "kind": "Constructor", + "name": "init", + "printedName": "init()", + "children": [ + { + "kind": "TypeNominal", + "name": "Greeter", + "printedName": "Hello.Greeter", + "usr": "s:5Hello7GreeterC" + } + ], + "declKind": "Constructor", + "usr": "s:5Hello7GreeterCACycfc", + "mangledName": "$s5Hello7GreeterCACycfc", + "moduleName": "Hello", + "declAttributes": [ + "AccessControl" + ], + "init_kind": "Designated" + }, + { + "kind": "Function", + "name": "hello", + "printedName": "hello()", + "children": [ + { + "kind": "TypeNominal", + "name": "Void", + "printedName": "()" + } + ], + "declKind": "Func", + "usr": "s:5Hello7GreeterC5helloyyF", + "mangledName": "$s5Hello7GreeterC5helloyyF", + "moduleName": "Hello", + "declAttributes": [ + "AccessControl" + ], + "funcSelfKind": "NonMutating" + } + ], + "declKind": "Class", + "usr": "s:5Hello7GreeterC", + "mangledName": "$s5Hello7GreeterC", + "moduleName": "Hello", + "declAttributes": [ + "AccessControl" + ], + "conformances": [ + { + "kind": "Conformance", + "name": "Copyable", + "printedName": "Copyable", + "usr": "s:s8CopyableP", + "mangledName": "$ss8CopyableP" + }, + { + "kind": "Conformance", + "name": "Escapable", + "printedName": "Escapable", + "usr": "s:s9EscapableP", + "mangledName": "$ss9EscapableP" + } + ] + } + ], + "json_format_version": 8 + }, + "ConstValues": [] +} \ No newline at end of file diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftdoc b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftdoc new file mode 100755 index 00000000000..5b333ebebe2 Binary files /dev/null and b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftdoc differ diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/arm64.private.swiftinterface b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/arm64.private.swiftinterface new file mode 100755 index 00000000000..e103ededecb --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/arm64.private.swiftinterface @@ -0,0 +1,12 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 6.0.2 effective-5.10 (swiftlang-6.0.2.1.2 clang-1600.0.26.4) +// swift-module-flags: -target arm64-apple-macosx10.9.0 -enable-objc-interop -enable-library-evolution -module-name Hello +import Swift +import _Concurrency +import _StringProcessing +import _SwiftConcurrencyShims +public class Greeter { + public init() + public func hello() + @objc deinit +} diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/arm64.swiftinterface b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/arm64.swiftinterface new file mode 100755 index 00000000000..e103ededecb --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/arm64.swiftinterface @@ -0,0 +1,12 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 6.0.2 effective-5.10 (swiftlang-6.0.2.1.2 clang-1600.0.26.4) +// swift-module-flags: -target arm64-apple-macosx10.9.0 -enable-objc-interop -enable-library-evolution -module-name Hello +import Swift +import _Concurrency +import _StringProcessing +import _SwiftConcurrencyShims +public class Greeter { + public init() + public func hello() + @objc deinit +} diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/arm64.swiftmodule b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/arm64.swiftmodule new file mode 100755 index 00000000000..2e8c35c0de0 Binary files /dev/null and b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/arm64.swiftmodule differ diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/x86_64.private.swiftinterface b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/x86_64.private.swiftinterface new file mode 100755 index 00000000000..3b82bc74568 --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/x86_64.private.swiftinterface @@ -0,0 +1,12 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 6.0.2 effective-5.10 (swiftlang-6.0.2.1.2 clang-1600.0.26.4) +// swift-module-flags: -target x86_64-apple-macosx10.9.0 -enable-objc-interop -enable-library-evolution -module-name Hello +import Swift +import _Concurrency +import _StringProcessing +import _SwiftConcurrencyShims +public class Greeter { + public init() + public func hello() + @objc deinit +} diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/x86_64.swiftinterface b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/x86_64.swiftinterface new file mode 100755 index 00000000000..3b82bc74568 --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/x86_64.swiftinterface @@ -0,0 +1,12 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 6.0.2 effective-5.10 (swiftlang-6.0.2.1.2 clang-1600.0.26.4) +// swift-module-flags: -target x86_64-apple-macosx10.9.0 -enable-objc-interop -enable-library-evolution -module-name Hello +import Swift +import _Concurrency +import _StringProcessing +import _SwiftConcurrencyShims +public class Greeter { + public init() + public func hello() + @objc deinit +} diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/x86_64.swiftmodule b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/x86_64.swiftmodule new file mode 100755 index 00000000000..7e6a7ed96c5 Binary files /dev/null and b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftmodule/x86_64.swiftmodule differ diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftsourceinfo b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftsourceinfo new file mode 100755 index 00000000000..97c726d55b0 Binary files /dev/null and b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/Hello.swiftsourceinfo differ diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/module.modulemap b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/module.modulemap new file mode 100755 index 00000000000..6b8a1ac76fd --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,4 @@ +framework module Hello { + header "HelloFramework.h" + export * +} diff --git a/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/Current b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/Current new file mode 120000 index 00000000000..8c7e5a667f1 --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/lib/Release/Hello.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/test/fixtures/test-app-symlink-framework/hello-world/package.json b/test/fixtures/test-app-symlink-framework/hello-world/package.json new file mode 100644 index 00000000000..1c18ca9ba87 --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/hello-world/package.json @@ -0,0 +1,8 @@ +{ + "name": "stripped-native-dep", + "version": "1.0.0", + "description": "", + "author": "", + "license": "ISC" + } + \ No newline at end of file diff --git a/test/fixtures/test-app-symlink-framework/index.js b/test/fixtures/test-app-symlink-framework/index.js new file mode 100644 index 00000000000..73c02658c48 --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/index.js @@ -0,0 +1 @@ +console.log('Hello World'); \ No newline at end of file diff --git a/test/fixtures/test-app-symlink-framework/package.json b/test/fixtures/test-app-symlink-framework/package.json new file mode 100644 index 00000000000..3e23fae548a --- /dev/null +++ b/test/fixtures/test-app-symlink-framework/package.json @@ -0,0 +1,18 @@ +{ + "name": "TestApp", + "productName": "Test App ßW", + "version": "1.1.0", + "homepage": "http://foo.example.com", + "description": "Test Application (test quite \" #378)", + "author": "Foo Bar ", + "license": "MIT", + "main": "index.js", + "build": { + "electronVersion": "23.3.10", + "appId": "org.electron-builder.testApp", + "compression": "store" + }, + "dependencies": { + "hello-world": "file:hello-world" + } +} diff --git a/test/snapshots/globTest.js.snap b/test/snapshots/globTest.js.snap index 345f1321e95..897d306549f 100644 --- a/test/snapshots/globTest.js.snap +++ b/test/snapshots/globTest.js.snap @@ -9792,13 +9792,242 @@ exports[`link 2`] = ` } `; +exports[`link 3`] = ` +{ + "files": { + "app": { + "files": { + "package.json": { + "files": { + "readme.md": { + "offset": "0", + "size": 32, + }, + }, + }, + "readme.md": { + "offset": "32", + "size": 78, + }, + }, + }, + "foo.js": { + "link": "index.js", + }, + "index.html": { + "offset": "110", + "size": 841, + }, + "index.js": { + "offset": "951", + "size": 4184, + }, + "package.json": { + "offset": "5135", + "size": 256, + }, + }, +} +`; + exports[`local node module with file protocol 1`] = ` { "linux": [], } `; -exports[`outside link 1`] = `"Cannot copy file (foo) symlinked to file (foo) outside the package as that violates asar security integrity"`; +exports[`symlinks everywhere w/ static framework 1`] = ` +{ + "linux": [], +} +`; + +exports[`symlinks everywhere w/ static framework 2`] = ` +{ + "link": "index.js", +} +`; + +exports[`symlinks everywhere w/ static framework 3`] = ` +{ + "files": { + "foo.js": { + "link": "index.js", + }, + "index.js": { + "size": "", + }, + "node_modules": { + "files": { + "debug": { + "files": { + "LICENSE": { + "size": "", + }, + "dist": { + "files": { + "debug.js": { + "size": "", + }, + }, + }, + "package.json": { + "size": "", + }, + "src": { + "files": { + "browser.js": { + "size": "", + }, + "common.js": { + "size": "", + }, + "index.js": { + "size": "", + }, + "node.js": { + "size": "", + }, + }, + }, + }, + }, + "hello-world": { + "files": { + "hello-world-framework.sh": { + "size": "", + }, + "lib": { + "files": { + "Release": { + "files": { + "Hello.framework": { + "files": { + "Headers": { + "link": "node_modules/hello-world/lib/Release/Hello.framework/Versions/Current/Headers", + }, + "Hello": { + "link": "node_modules/hello-world/lib/Release/Hello.framework/Versions/Current/Hello", + }, + "Info.plist": { + "link": "node_modules/hello-world/lib/Release/Hello.framework/Versions/Current/Info.plist", + }, + "Modules": { + "link": "node_modules/hello-world/lib/Release/Hello.framework/Versions/Current/Modules", + }, + "Versions": { + "files": { + "A": { + "files": { + "Headers": { + "files": { + "HelloFramework.h": { + "executable": true, + "size": "", + }, + }, + }, + "Hello": { + "executable": true, + "size": "", + }, + "Info.plist": { + "executable": true, + "size": "", + }, + "Modules": { + "files": { + "Hello.abi.json": { + "executable": true, + "size": "", + }, + "Hello.swiftdoc": { + "executable": true, + "size": "", + }, + "Hello.swiftmodule": { + "files": { + "arm64.private.swiftinterface": { + "executable": true, + "size": "", + }, + "arm64.swiftinterface": { + "executable": true, + "size": "", + }, + "arm64.swiftmodule": { + "executable": true, + "size": "", + }, + "x86_64.private.swiftinterface": { + "executable": true, + "size": "", + }, + "x86_64.swiftinterface": { + "executable": true, + "size": "", + }, + "x86_64.swiftmodule": { + "executable": true, + "size": "", + }, + }, + }, + "Hello.swiftsourceinfo": { + "executable": true, + "size": "", + }, + "module.modulemap": { + "executable": true, + "size": "", + }, + }, + }, + }, + }, + "Current": { + "link": "node_modules/hello-world/lib/Release/Hello.framework/Versions/A", + }, + }, + }, + }, + }, + }, + }, + }, + }, + "package.json": { + "size": "", + }, + }, + }, + "ms": { + "files": { + "index.js": { + "size": "", + }, + "license.md": { + "size": "", + }, + "package.json": { + "size": "", + }, + }, + }, + }, + }, + "package.json": { + "size": "", + }, + }, +} +`; + +exports[`symlinks everywhere w/ static framework 4`] = ` +[ + "app.asar", +] +`; exports[`unpackDir 1`] = ` { diff --git a/test/src/globTest.ts b/test/src/globTest.ts index 5371b96b740..973e2359fc4 100644 --- a/test/src/globTest.ts +++ b/test/src/globTest.ts @@ -85,7 +85,7 @@ test.ifDevOrLinuxCi("asarUnpack and files ignore", () => { const resourceDir = context.getResources(Platform.LINUX) await Promise.all([assertThat(path.join(resourceDir, "app.asar.unpacked", "node_modules/ffprobe-static/bin/darwin/x64/ffprobe")).doesNotExist()]) - await verifyAsarFileTree(context.getResources(Platform.LINUX)) + await verifyAsarFileTree(resourceDir) }, } ) @@ -102,7 +102,38 @@ test.ifNotWindows( return fs.symlink(path.join(projectDir, "index.js"), path.join(projectDir, "foo.js")) }, packed: async context => { - expect((await readAsar(path.join(context.getResources(Platform.LINUX), "app.asar"))).getFile("foo.js", false)).toMatchSnapshot() + const resources = context.getResources(Platform.LINUX) + expect((await readAsar(path.join(resources, "app.asar"))).getFile("foo.js", false)).toMatchSnapshot() + await verifyAsarFileTree(resources) + }, + } + ) +) + +test.ifNotWindows( + "symlinks everywhere w/ static framework", () => + assertPack("test-app-symlink-framework", + { + targets: Platform.LINUX.createTarget(DIR_TARGET), + config: { + files: ["!hello-world"] + } + }, + { + isInstallDepsBefore: true, + projectDirCreated: async projectDir => { + await modifyPackageJson(projectDir, data => { + data.dependencies = { + debug: "4.1.1", + ...data.dependencies + } + }) + return fs.symlink(path.join(projectDir, "index.js"), path.join(projectDir, "foo.js")) + }, + packed: async context => { + const resources = context.getResources(Platform.LINUX) + expect((await readAsar(path.join(resources, "app.asar"))).getFile("foo.js", false)).toMatchSnapshot() + await verifySmartUnpack(resources) }, } ) @@ -120,7 +151,8 @@ test.ifNotWindows( await outputFile(path.join(tempDir, "foo"), "data") await fs.symlink(tempDir, path.join(projectDir, "o-dir")) }, - } + }, + error => expect(error.message).toContain("violates asar security integrity") ) )