diff --git a/Sources/LinuxPlatform/Linux.swift b/Sources/LinuxPlatform/Linux.swift index 391bdc36..7d65ac09 100644 --- a/Sources/LinuxPlatform/Linux.swift +++ b/Sources/LinuxPlatform/Linux.swift @@ -20,12 +20,12 @@ public struct Linux: Platform { public init() {} - public var appDataDirectory: URL { + public var defaultSwiftlyHomeDirectory: URL { if let dir = ProcessInfo.processInfo.environment["XDG_DATA_HOME"] { - return URL(fileURLWithPath: dir) + return URL(fileURLWithPath: dir).appendingPathComponent("swiftly", isDirectory: true) } else { return FileManager.default.homeDirectoryForCurrentUser - .appendingPathComponent(".local/share", isDirectory: true) + .appendingPathComponent(".local/share/swiftly", isDirectory: true) } } diff --git a/Sources/MacOSPlatform/MacOS.swift b/Sources/MacOSPlatform/MacOS.swift index ae32b34f..fa4b075d 100644 --- a/Sources/MacOSPlatform/MacOS.swift +++ b/Sources/MacOSPlatform/MacOS.swift @@ -13,16 +13,16 @@ public struct SwiftPkgInfo: Codable { public struct MacOS: Platform { public init() {} - public var appDataDirectory: URL { + public var defaultSwiftlyHomeDirectory: URL { FileManager.default.homeDirectoryForCurrentUser - .appendingPathComponent("Library/Application Support", isDirectory: true) + .appendingPathComponent(".swiftly", isDirectory: true) } public var swiftlyBinDir: URL { SwiftlyCore.mockedHomeDir.map { $0.appendingPathComponent("bin", isDirectory: true) } ?? ProcessInfo.processInfo.environment["SWIFTLY_BIN_DIR"].map { URL(fileURLWithPath: $0) } ?? FileManager.default.homeDirectoryForCurrentUser - .appendingPathComponent("Library/Application Support/swiftly/bin", isDirectory: true) + .appendingPathComponent(".swiftly/bin", isDirectory: true) } public var swiftlyToolchainsDir: URL { @@ -99,7 +99,7 @@ public struct MacOS: Platform { } else { homeDir = SwiftlyCore.mockedHomeDir ?? FileManager.default.homeDirectoryForCurrentUser - let installDir = homeDir.appendingPathComponent("usr/local") + let installDir = homeDir.appendingPathComponent(".swiftly") try FileManager.default.createDirectory(atPath: installDir.path, withIntermediateDirectories: true) // In the case of a mock for testing purposes we won't use the installer, perferring a manual process because @@ -114,10 +114,11 @@ public struct MacOS: Platform { throw SwiftlyError(message: "Payload file could not be found at \(tmpDir).") } - try runProgram("tar", "-C", installDir.path, "-xf", payload.path) + SwiftlyCore.print("Extracting the swiftly package into \(installDir.path)...") + try runProgram("tar", "-C", installDir.path, "-xvf", payload.path, quiet: false) } - try self.runProgram(homeDir.appendingPathComponent("usr/local/bin/swiftly").path, "init") + try self.runProgram(homeDir.appendingPathComponent(".swiftly/bin/swiftly").path, "init") } public func uninstall(_ toolchain: ToolchainVersion, verbose: Bool) throws { diff --git a/Sources/Swiftly/Init.swift b/Sources/Swiftly/Init.swift index a9c6f54e..35dd29f1 100644 --- a/Sources/Swiftly/Init.swift +++ b/Sources/Swiftly/Init.swift @@ -80,7 +80,7 @@ internal struct Init: SwiftlyCommand { \(Swiftly.currentPlatform.swiftlyHomeDir.path) - Data and configuration files directory including toolchains \(Swiftly.currentPlatform.swiftlyBinDir.path) - Executables installation directory - These locations can be changed with SWIFTLY_HOME and SWIFTLY_BIN environment variables and run this again. + These locations can be changed with SWIFTLY_HOME_DIR and SWIFTLY_BIN_DIR environment variables and run this again. \(installMsg) """) @@ -89,22 +89,6 @@ internal struct Init: SwiftlyCommand { } } - // Ensure swiftly doesn't overwrite any existing executables without getting confirmation first. - let swiftlyBinDir = Swiftly.currentPlatform.swiftlyBinDir - let swiftlyBinDirContents = (try? FileManager.default.contentsOfDirectory(atPath: swiftlyBinDir.path)) ?? [String]() - let willBeOverwritten = Set(["swiftly"]).intersection(swiftlyBinDirContents) - if !willBeOverwritten.isEmpty && !overwrite { - SwiftlyCore.print("The following existing executables will be overwritten:") - - for executable in willBeOverwritten { - SwiftlyCore.print(" \(swiftlyBinDir.appendingPathComponent(executable).path)") - } - - guard SwiftlyCore.promptForConfirmation(defaultBehavior: false) else { - throw SwiftlyError(message: "Swiftly installation has been cancelled") - } - } - let shell = if let s = ProcessInfo.processInfo.environment["SHELL"] { s } else { @@ -239,14 +223,16 @@ internal struct Init: SwiftlyCommand { (postInstall, pathChanged) = try await Install.execute(version: latestVersion, &config, useInstalledToolchain: true, verifySignature: true, verbose: verbose, assumeYes: assumeYes) } - if addEnvToProfile && !quietShellFollowup { + if addEnvToProfile { try Data(sourceLine.utf8).append(to: profileHome) - SwiftlyCore.print(""" - To begin using installed swiftly from your current shell, first run the following command: - \(sourceLine) + if !quietShellFollowup { + SwiftlyCore.print(""" + To begin using installed swiftly from your current shell, first run the following command: + \(sourceLine) - """) + """) + } } // Fish doesn't have path caching, so this might only be needed for bash/zsh diff --git a/Sources/SwiftlyCore/Platform.swift b/Sources/SwiftlyCore/Platform.swift index f9d7c7ff..4b7291f9 100644 --- a/Sources/SwiftlyCore/Platform.swift +++ b/Sources/SwiftlyCore/Platform.swift @@ -39,9 +39,9 @@ public struct RunProgramError: Swift.Error { } public protocol Platform { - /// The platform-specific location on disk where applications are - /// supposed to store their custom data. - var appDataDirectory: URL { get } + /// The platform-specific defaut location on disk for swiftly's home + /// directory. + var defaultSwiftlyHomeDirectory: URL { get } /// The directory which stores the swiftly executable itself as well as symlinks /// to executables in the "bin" directory of the active toolchain. @@ -130,7 +130,7 @@ extension Platform { public var swiftlyHomeDir: URL { SwiftlyCore.mockedHomeDir ?? ProcessInfo.processInfo.environment["SWIFTLY_HOME_DIR"].map { URL(fileURLWithPath: $0) } - ?? self.appDataDirectory.appendingPathComponent("swiftly", isDirectory: true) + ?? self.defaultSwiftlyHomeDirectory } /// The URL of the configuration file in swiftly's home directory. diff --git a/Tests/SwiftlyTests/SwiftlyTests.swift b/Tests/SwiftlyTests/SwiftlyTests.swift index 3a5b4d14..6d6b819b 100644 --- a/Tests/SwiftlyTests/SwiftlyTests.swift +++ b/Tests/SwiftlyTests/SwiftlyTests.swift @@ -729,7 +729,7 @@ public class MockToolchainDownloader: HTTPRequestExecutor { #elseif os(macOS) public func makeMockedSwiftly(from _: URL) throws -> Data { let tmp = FileManager.default.temporaryDirectory.appendingPathComponent("swiftly-\(UUID())") - let swiftlyDir = tmp.appendingPathComponent("swiftly", isDirectory: true) + let swiftlyDir = tmp.appendingPathComponent(".swiftly", isDirectory: true) let swiftlyBinDir = swiftlyDir.appendingPathComponent("bin") try FileManager.default.createDirectory( @@ -766,7 +766,7 @@ public class MockToolchainDownloader: HTTPRequestExecutor { "--root", swiftlyDir.path, "--install-location", - "usr/local", + ".swiftly", "--version", "\(self.latestSwiftlyVersion)", "--identifier", diff --git a/Tests/SwiftlyTests/UninstallTests.swift b/Tests/SwiftlyTests/UninstallTests.swift index 4a97798e..2162e0dd 100644 --- a/Tests/SwiftlyTests/UninstallTests.swift +++ b/Tests/SwiftlyTests/UninstallTests.swift @@ -301,10 +301,10 @@ final class UninstallTests: SwiftlyTests { func testUninstallNotInstalled() async throws { let toolchains = Set([Self.oldStable, Self.newStable, Self.newMainSnapshot, Self.oldReleaseSnapshot]) try await self.withMockedHome(homeName: Self.homeName, toolchains: toolchains, inUse: Self.newMainSnapshot) { - var config = try await Config.load() + var config = try Config.load() config.inUse = Self.newMainSnapshot config.installedToolchains.remove(Self.newMainSnapshot) - try await config.save() + try config.save() var uninstall = try self.parseCommand(Uninstall.self, ["uninstall", "-y", Self.newMainSnapshot.name]) _ = try await uninstall.run() diff --git a/Tools/build-swiftly-release/BuildSwiftlyRelease.swift b/Tools/build-swiftly-release/BuildSwiftlyRelease.swift index 9f06ade5..38a863f0 100644 --- a/Tools/build-swiftly-release/BuildSwiftlyRelease.swift +++ b/Tools/build-swiftly-release/BuildSwiftlyRelease.swift @@ -399,12 +399,12 @@ struct BuildSwiftlyRelease: AsyncParsableCommand { try runProgram(strip, ".build/\(arch)-apple-macosx/release/swiftly") } - let swiftlyBinDir = FileManager.default.currentDirectoryPath + "/.build/release/usr/local/bin" + let swiftlyBinDir = FileManager.default.currentDirectoryPath + "/.build/release/.swiftly/bin" try? FileManager.default.createDirectory(atPath: swiftlyBinDir, withIntermediateDirectories: true) try runProgram(lipo, ".build/x86_64-apple-macosx/release/swiftly", ".build/arm64-apple-macosx/release/swiftly", "-create", "-o", "\(swiftlyBinDir)/swiftly") - let swiftlyLicenseDir = FileManager.default.currentDirectoryPath + "/.build/release/usr/local/share/doc/swiftly/license" + let swiftlyLicenseDir = FileManager.default.currentDirectoryPath + "/.build/release/.swiftly/license" try? FileManager.default.createDirectory(atPath: swiftlyLicenseDir, withIntermediateDirectories: true) try await self.collectLicenses(swiftlyLicenseDir) @@ -418,7 +418,7 @@ struct BuildSwiftlyRelease: AsyncParsableCommand { "--root", swiftlyBinDir + "/..", "--install-location", - "usr/local", + ".swiftly", "--version", self.version, "--identifier", @@ -433,7 +433,7 @@ struct BuildSwiftlyRelease: AsyncParsableCommand { "--root", swiftlyBinDir + "/..", "--install-location", - "usr/local", + ".swiftly", "--version", self.version, "--identifier", @@ -455,9 +455,9 @@ struct BuildSwiftlyRelease: AsyncParsableCommand { try distFileContents.write(to: distFile, atomically: true, encoding: .utf8) if let cert = cert { - try runProgram("productbuild", "--distribution", distFile.path, "--package-path", pkgFile.path, "--sign", cert, pkgFileReconfigured.path) + try runProgram("productbuild", "--distribution", distFile.path, "--package-path", pkgFile.deletingLastPathComponent().path, "--sign", cert, pkgFileReconfigured.path) } else { - try runProgram("productbuild", "--distribution", distFile.path, "--package-path", pkgFile.path, pkgFileReconfigured.path) + try runProgram("productbuild", "--distribution", distFile.path, "--package-path", pkgFile.deletingLastPathComponent().path, pkgFileReconfigured.path) } try FileManager.default.removeItem(at: pkgFile) try FileManager.default.copyItem(atPath: pkgFileReconfigured.path, toPath: pkgFile.path)