2024-06-20 08:55:55 +03:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2024 by Claudio Cambra <claudio.cambra@nextcloud.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* for more details.
|
|
|
|
*/
|
|
|
|
|
2024-06-20 09:16:58 +03:00
|
|
|
import ArgumentParser
|
2024-06-20 08:50:12 +03:00
|
|
|
import Foundation
|
|
|
|
|
2024-06-20 09:16:58 +03:00
|
|
|
struct MacCrafter: ParsableCommand {
|
|
|
|
static let configuration = CommandConfiguration(
|
2024-06-20 09:59:11 +03:00
|
|
|
abstract: "A tool to easily build a fully-functional Nextcloud Desktop Client for macOS."
|
2024-06-20 09:16:58 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
enum MacCrafterError: Error {
|
|
|
|
case failedEnumeration(String)
|
2024-06-20 13:20:26 +03:00
|
|
|
case environmentError(String)
|
2024-06-20 09:16:58 +03:00
|
|
|
}
|
|
|
|
|
2024-06-20 09:58:14 +03:00
|
|
|
@Argument(help: "Path to the root directory of the Nextcloud Desktop Client git repository.")
|
|
|
|
var repoRootDir = "\(FileManager.default.currentDirectoryPath)/../../.."
|
|
|
|
|
|
|
|
@Option(name: [.short, .long], help: "Code signing identity for desktop client and libs.")
|
|
|
|
var codeSignIdentity: String?
|
|
|
|
|
|
|
|
@Option(name: [.short, .customLong("buildPath")], help: "Path for build files to be written.")
|
|
|
|
var buildPath = "\(FileManager.default.currentDirectoryPath)/build"
|
|
|
|
|
|
|
|
@Option(name: [.long], help: "Brew installation script URL.")
|
|
|
|
var brewInstallShUrl = "https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh"
|
|
|
|
|
|
|
|
@Option(name: [.long], help: "CraftMaster git url.")
|
|
|
|
var craftMasterGitUrl = "https://invent.kde.org/packaging/craftmaster.git"
|
|
|
|
|
|
|
|
@Option(name: [.long], help: "Nextcloud Desktop Client git url.")
|
|
|
|
var clientBlueprintsGitUrl = "https://github.com/nextcloud/desktop-client-blueprints.git"
|
2024-06-20 09:27:25 +03:00
|
|
|
|
2024-06-20 13:09:48 +03:00
|
|
|
@Option(name: [.long], help: "Build type (e.g. Release, RelWithDebInfo, MinSizeRel, Debug).")
|
|
|
|
var buildType = "RelWithDebInfo"
|
|
|
|
|
2024-06-20 13:11:07 +03:00
|
|
|
@Option(name: [.long], help: "Skip code-signing dependency libraries and plugins.")
|
|
|
|
var skipDependencyCodeSigning = false
|
|
|
|
|
2024-06-20 13:11:33 +03:00
|
|
|
@Option(name: [.long], help: "The application's branded name.")
|
|
|
|
var appName = "Nextcloud"
|
|
|
|
|
2024-06-20 09:16:58 +03:00
|
|
|
mutating func run() throws {
|
|
|
|
print("Configuring build tooling.")
|
|
|
|
|
2024-06-20 13:30:57 +03:00
|
|
|
if codeSignIdentity != nil {
|
2024-06-20 13:20:26 +03:00
|
|
|
guard commandExists("codesign") else {
|
|
|
|
throw MacCrafterError.environmentError("codesign not found, cannot proceed.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-20 09:25:56 +03:00
|
|
|
try installIfMissing("git", "xcode-select --install")
|
|
|
|
try installIfMissing(
|
2024-06-20 09:16:58 +03:00
|
|
|
"brew",
|
2024-06-20 10:59:10 +03:00
|
|
|
"curl -fsSL \(brewInstallShUrl) | /bin/bash",
|
2024-06-20 09:16:58 +03:00
|
|
|
installCommandEnv: ["NONINTERACTIVE": "1"]
|
|
|
|
)
|
2024-06-20 09:25:56 +03:00
|
|
|
try installIfMissing("inkscape", "brew install inkscape")
|
|
|
|
try installIfMissing("python3", "brew install pyenv && pyenv install 3.12.4")
|
2024-06-20 09:16:58 +03:00
|
|
|
|
|
|
|
print("Build tooling configured.")
|
|
|
|
print("Configuring KDE Craft.")
|
2024-06-20 08:50:12 +03:00
|
|
|
|
2024-06-20 10:59:10 +03:00
|
|
|
let craftMasterDir = "\(buildPath)/craftmaster"
|
2024-06-20 09:16:58 +03:00
|
|
|
let fm = FileManager.default
|
2024-06-20 08:50:12 +03:00
|
|
|
|
2024-06-20 09:16:58 +03:00
|
|
|
if fm.fileExists(atPath: craftMasterDir) {
|
|
|
|
print("KDE Craft is already cloned.")
|
|
|
|
} else {
|
|
|
|
print("Cloning KDE Craft...")
|
2024-06-20 09:43:27 +03:00
|
|
|
shell("git clone --depth=1 \(craftMasterGitUrl) \(craftMasterDir)")
|
2024-06-20 09:16:58 +03:00
|
|
|
}
|
2024-06-20 08:50:12 +03:00
|
|
|
|
2024-06-20 09:16:58 +03:00
|
|
|
print("Configuring Nextcloud Desktop Client blueprints for KDE Craft...")
|
2024-06-20 08:50:12 +03:00
|
|
|
|
2024-06-20 09:16:58 +03:00
|
|
|
let craftMasterIni = "\(repoRootDir)/craftmaster.ini"
|
|
|
|
let craftMasterPy = "\(craftMasterDir)/CraftMaster.py"
|
|
|
|
let craftTarget = "macos-clang-arm64"
|
2024-06-20 09:43:27 +03:00
|
|
|
let craftCommand =
|
|
|
|
"python3 \(craftMasterPy) --config \(craftMasterIni) --target \(craftTarget) -c"
|
2024-06-20 09:16:58 +03:00
|
|
|
shell("\(craftCommand) --add-blueprint-repository \(clientBlueprintsGitUrl)")
|
|
|
|
|
|
|
|
print("Crafting KDE Craft...")
|
|
|
|
shell("\(craftCommand) craft")
|
|
|
|
|
|
|
|
print("Crafting Nextcloud Desktop Client dependencies...")
|
|
|
|
shell("\(craftCommand) --install-deps nextcloud-client")
|
|
|
|
|
2024-06-20 13:11:07 +03:00
|
|
|
if !skipDependencyCodeSigning, let codeSignIdentity {
|
2024-06-20 09:58:14 +03:00
|
|
|
print("Code-signing Nextcloud Desktop Client libraries and frameworks...")
|
|
|
|
|
2024-06-20 10:59:10 +03:00
|
|
|
let craftLibDir = "\(buildPath)/\(craftTarget)/lib"
|
2024-06-20 09:58:14 +03:00
|
|
|
let craftLibs = try fm.contentsOfDirectory(atPath: craftLibDir)
|
|
|
|
for lib in craftLibs {
|
|
|
|
guard isLibrary(lib) else { continue }
|
|
|
|
try codesign(identity: codeSignIdentity, path: "\(craftLibDir)/\(lib)")
|
|
|
|
}
|
|
|
|
|
2024-06-20 10:59:10 +03:00
|
|
|
let craftPluginsDir = "\(buildPath)/\(craftTarget)/plugins"
|
2024-06-20 09:58:14 +03:00
|
|
|
guard let craftPluginsEnumerator = fm.enumerator(atPath: craftPluginsDir) else {
|
2024-06-20 10:59:10 +03:00
|
|
|
throw MacCrafterError.failedEnumeration(
|
|
|
|
"Failed to list craft plugins directory at \(craftPluginsDir)."
|
|
|
|
)
|
2024-06-20 09:58:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
for case let plugin as String in craftPluginsEnumerator {
|
|
|
|
guard isLibrary(plugin) else { continue }
|
|
|
|
try codesign(identity: codeSignIdentity, path: "\(craftPluginsDir)/\(plugin)")
|
|
|
|
}
|
2024-06-20 09:27:37 +03:00
|
|
|
}
|
|
|
|
|
2024-06-20 09:16:58 +03:00
|
|
|
print("Crafting Nextcloud Desktop Client...")
|
2024-06-20 13:20:10 +03:00
|
|
|
shell("\(craftCommand) --src-dir \(repoRootDir) -i --buildtype \(buildType) nextcloud-client")
|
2024-06-20 13:11:33 +03:00
|
|
|
|
|
|
|
if let codeSignIdentity {
|
2024-06-20 13:31:39 +03:00
|
|
|
let craftBuildDir = "\(buildPath)/\(craftTarget)/build"
|
2024-06-20 13:11:33 +03:00
|
|
|
let clientAppDir =
|
2024-06-20 13:31:39 +03:00
|
|
|
"\(craftBuildDir)/nextcloud-client/image-\(buildType)-master/\(appName).app"
|
2024-06-20 13:11:33 +03:00
|
|
|
try codesign(identity: codeSignIdentity, path: clientAppDir)
|
|
|
|
}
|
2024-06-20 09:16:58 +03:00
|
|
|
}
|
|
|
|
}
|
2024-06-20 08:50:12 +03:00
|
|
|
|
2024-06-20 09:16:58 +03:00
|
|
|
MacCrafter.main()
|