-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from freddi-kit/first-commit
First commit
- Loading branch information
Showing
16 changed files
with
479 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
.DS_Store | ||
/.build | ||
/Packages | ||
/*.xcodeproj | ||
xcuserdata/ | ||
DerivedData/ | ||
.swiftpm/config/registries.json | ||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata | ||
.netrc |
8 changes: 8 additions & 0 deletions
8
.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>IDEDidComputeMac32BitWarning</key> | ||
<true/> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// swift-tools-version: 5.7 | ||
// The swift-tools-version declares the minimum version of Swift required to build this package. | ||
|
||
import PackageDescription | ||
|
||
let package = Package( | ||
name: "ArtifactBundleGen", | ||
products: [ | ||
.plugin(name: "ArtifactBundleGenCommand", targets: ["ArtifactBundleGenCommand"]), | ||
.library(name: "ArtifactBundleGen", targets: ["ArtifactBundleGen"]) | ||
], | ||
dependencies: [ | ||
], | ||
targets: [ | ||
.target( | ||
name: "ArtifactBundleGen", | ||
dependencies: []), | ||
.testTarget( | ||
name: "ArtifactBundleGenTests", | ||
dependencies: ["ArtifactBundleGen"]), | ||
.plugin( | ||
name: "ArtifactBundleGenCommand", | ||
capability: .command( | ||
intent: .custom(verb: "generate-artifact-bundle", | ||
description: "Generate Artifact Bundle"), | ||
permissions: [.writeToPackageDirectory(reason: "Save Artifact Bundle information")] | ||
), | ||
path: "Plugins/ArtifactBundleGenCommand" | ||
), | ||
] | ||
) |
32 changes: 32 additions & 0 deletions
32
Plugins/ArtifactBundleGenCommand/ArtifactBundleGenCommand.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import PackagePlugin | ||
import Foundation | ||
import OSLog | ||
|
||
@main | ||
struct ArtifactBundleGenCommand: CommandPlugin { | ||
|
||
func performCommand(context: PluginContext, arguments: [String]) throws { | ||
var argumentExtractor = ArgumentExtractor(arguments) | ||
|
||
let packageVersionOption = argumentExtractor.extractOption(named: "package-version") | ||
let packageNameOption = argumentExtractor.extractOption(named: "package-name") | ||
let buildFolderNameOption = argumentExtractor.extractOption(named: "build-folder") | ||
let configOption = argumentExtractor.extractOption(named: "build-config") | ||
|
||
guard let name = packageNameOption.first else { | ||
throw ArtifactBundleGenError.nameOptionMissing | ||
} | ||
|
||
guard let configString = configOption.first, let config = Config(rawValue: configString) else { | ||
throw ArtifactBundleGenError.configOptionParseError(configString: configOption.first ?? "{empty}") | ||
} | ||
|
||
let artifactBundleGen = ArtifactBundleGen( | ||
version: packageVersionOption.first ?? "1.0.0", | ||
name: name, | ||
buildFolderName: buildFolderNameOption.first ?? ".build", | ||
config: config | ||
) | ||
try artifactBundleGen.generate() | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
Plugins/ArtifactBundleGenCommand/Symbolic Links/ArtifactBundleGen
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../Sources/ArtifactBundleGen |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# ArtifactBundleGen (Beta) | ||
|
||
Generates Artifact Bundle from Swift Package (Executable only) | ||
|
||
## Example | ||
|
||
```sh | ||
# Go to Package directory. it is expeced that ArtifactBundleGen is added as command plugin in Package.swift | ||
$ cd some-awesome-tool | ||
|
||
# Call build command | ||
$ swift build -c debug --arch arm64 --arch x86_64 | ||
|
||
# .build is generated | ||
|
||
# Call command | ||
$ swift package generate-artifact-bundle --package-version 0.5.11 --package-name some-awesome-tool --build-config debug --build-folder .build | ||
|
||
$ ls | ||
> some-awesome-tool.artifactbundle | ||
|
||
# zip it and release! | ||
$ zip -r some-awesome-tool.artifactbundle.zip some-awesome-tool.artifactbundle | ||
``` | ||
|
||
## Usase | ||
### 1. Add ArtifactBundleGen as plugin in Package.swift | ||
|
||
```swift | ||
let package = Package( | ||
name: "XcodeGen", | ||
products: [ | ||
// ... some products | ||
], | ||
dependencies: [ | ||
// ... some dependencies | ||
.package(url: "https://github.com/freddi-kit/ArtifactBundleGen.git", .exact("0.0.1")) | ||
], | ||
|
||
``` | ||
|
||
### 2. Build your product in Shell | ||
|
||
```sh | ||
# Call build command | ||
$ swift build -c debug --arch arm64 --arch x86_64 | ||
``` | ||
|
||
|
||
### 3. Call ArtifactBundleGen | ||
|
||
```sh | ||
$ swift package generate-artifact-bundle --package-version {version} --package-name {tool_name} --build-config {config} --build-folder {folder} | ||
``` | ||
|
||
{tool_name}.artifactbundle will be generated! | ||
|
||
#### Opitions | ||
- --package-name: Name of Aritfact Bundle. Please specify executable tool's name | ||
- --build-config: build config. `debug` or `release` | ||
|
||
##### optionals | ||
- --package-version: version of package (default is 1.0.0) | ||
- --build-folder: version of package (default is .build) | ||
|
||
|
||
### 4. Zip Artifact Bundle and release the zip! | ||
|
||
```sh | ||
$ zip -r {tool_name}.artifactbundle.zip {tool_name}.artifactbundle | ||
``` | ||
|
||
|
||
## TODOs | ||
- [ ] include LICENSE file | ||
- [ ] Suport other type |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import Foundation | ||
|
||
public struct ArtifactBundleGen { | ||
|
||
private let version: String | ||
private let name: String | ||
private let buildFolderName: String | ||
private let config: Config | ||
|
||
private let folderCreator = FolderCreator() | ||
private let lipoRunner = LipoRunnner() | ||
private let fileCopy = FileCopy() | ||
private let fileExistChecker = FileExistChecker() | ||
|
||
private var artifactBundleFolderName: String { | ||
"\(name).artifactbundle" | ||
} | ||
|
||
private var appleUniversalBinaryFolderName: String { | ||
"\(buildFolderName)/apple/Products/\(config.headUpperCase)" | ||
} | ||
|
||
private var appleUniversalBinaryPath: String { | ||
"\(appleUniversalBinaryFolderName)/xcodegen" | ||
} | ||
|
||
private func prepareArtifactBundleFolder() throws { | ||
try folderCreator.createFolder(name: artifactBundleFolderName) | ||
} | ||
|
||
private func generateAppleUniversalUniversalBinaryArchIfExists() throws -> [Variant] { | ||
guard fileExistChecker.isExist(path: appleUniversalBinaryPath) else { return [] } | ||
|
||
var variants: [Variant] = [] | ||
let supoortedArchs = try lipoRunner.chechArch(of: appleUniversalBinaryPath) | ||
|
||
let appBundleUniversalBinaryFolderName = "\(name)-\(version)-macosx" | ||
let destinationUniversalBinaryFolderName = "\(artifactBundleFolderName)/\(appBundleUniversalBinaryFolderName)/bin" | ||
try folderCreator.createFolder(name: destinationUniversalBinaryFolderName) | ||
|
||
let destinationUniversalBinaryPath = "\(destinationUniversalBinaryFolderName)/\(name)" | ||
let originExecutableURL = URL(fileURLWithPath: appleUniversalBinaryPath) | ||
let destinationURL = URL(fileURLWithPath: destinationUniversalBinaryPath) | ||
|
||
try fileCopy.copy(from: originExecutableURL, to: destinationURL) | ||
|
||
variants.append( | ||
Variant( | ||
path: "\(appBundleUniversalBinaryFolderName)/bin/\(name)", | ||
supportedTriples: supoortedArchs.map { | ||
"\($0)-apple-macosx" | ||
} | ||
) | ||
) | ||
|
||
return variants | ||
} | ||
|
||
private func generateEachTriplesVariantsIfExists() throws -> [Variant] { | ||
var variants: [Variant] = [] | ||
|
||
// check for all triples | ||
for triple in VariantTriples.triples { | ||
let executablePath = "\(buildFolderName)/\(triple)/\(config.rawValue)/\(name)" | ||
guard fileExistChecker.isExist(path: executablePath) else { continue } | ||
|
||
let artifactTripleDirectryPath = "\(artifactBundleFolderName)/\(triple)/bin" | ||
try folderCreator.createFolder(name: artifactTripleDirectryPath) | ||
|
||
let destinationPath = "\(artifactTripleDirectryPath)/\(name)" | ||
let originExecutableURL = URL(fileURLWithPath: executablePath) | ||
let destinationURL = URL(fileURLWithPath: destinationPath) | ||
|
||
try fileCopy.copy(from: originExecutableURL, to: destinationURL) | ||
|
||
variants.append( | ||
Variant( | ||
path: "\(triple)/bin/\(name)", | ||
supportedTriples: [triple] | ||
) | ||
) | ||
} | ||
|
||
return variants | ||
} | ||
|
||
private func generateArtifactBundle(variants: [Variant]) -> ArtifactBundle { | ||
let artifact = Artifact( | ||
version: version, | ||
type: .executable, | ||
variants: variants | ||
) | ||
|
||
return ArtifactBundle( | ||
schemaVersion: "1.0", | ||
artifacts: [name: artifact] | ||
) | ||
} | ||
|
||
private func encodeToJsonString(artifactBundle: ArtifactBundle) throws -> String { | ||
let jsonEncoder = JSONEncoder() | ||
jsonEncoder.outputFormatting = .prettyPrinted | ||
|
||
let jsonData = try jsonEncoder.encode(artifactBundle) | ||
|
||
guard let string = String(data: jsonData, encoding: .utf8) else { | ||
fatalError() | ||
} | ||
return string.replacingOccurrences(of: "\\", with: "") | ||
} | ||
|
||
public init(version: String, name: String, buildFolderName: String, config: Config) { | ||
self.version = version | ||
self.name = name | ||
self.buildFolderName = buildFolderName | ||
self.config = config | ||
} | ||
|
||
public func generate() throws { | ||
|
||
try prepareArtifactBundleFolder() | ||
|
||
var variants: [Variant] = [] | ||
|
||
let generatedAppleUniversalUniversalBinaryVariants = try generateAppleUniversalUniversalBinaryArchIfExists() | ||
variants.append(contentsOf: generatedAppleUniversalUniversalBinaryVariants) | ||
|
||
let generatedEachTriplesVariants = try generateEachTriplesVariantsIfExists() | ||
variants.append(contentsOf: generatedEachTriplesVariants) | ||
|
||
let artifactBundle = generateArtifactBundle(variants: variants) | ||
let destinationPath = URL(fileURLWithPath: "\(artifactBundleFolderName)/info.json") | ||
|
||
try encodeToJsonString(artifactBundle: artifactBundle) | ||
.write(to: destinationPath, atomically: true, encoding: .utf8) | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
Sources/ArtifactBundleGen/Error/ArtifactBundleGenError.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// | ||
// File.swift | ||
// | ||
// | ||
// Created by JP29872 on 2022/12/07. | ||
// | ||
|
||
import Foundation | ||
|
||
enum ArtifactBundleGenError: LocalizedError, CustomStringConvertible { | ||
case folderCreationFailure(folderName: String, error: Error) | ||
case fileCopyFailure(origin: String, destination: String, error: Error) | ||
|
||
case lipoFailure(error: Error) | ||
case lipoEmptyResult | ||
|
||
case nameOptionMissing | ||
case configOptionParseError(configString: String) | ||
|
||
var description: String { return errorDescription ?? "Unexpected Error" } | ||
|
||
var errorDescription: String? { | ||
switch self { | ||
case .folderCreationFailure(let folderName, let error): return "Failed to create folder named \"\(folderName)\" : \(error.localizedDescription)" | ||
case .fileCopyFailure(let origin, let destination, let error): return "Failed to copy file from \"\(origin)\" to \"\(destination)\" : \(error.localizedDescription)" | ||
case .lipoFailure(let error): return "Failed to run lipo: \(error.localizedDescription)" | ||
case .lipoEmptyResult: return "Error, lipo returns empty result" | ||
case .nameOptionMissing: return "Please specify to name by --package-name" | ||
case .configOptionParseError(let configString): return "Failed to parse config specified by --build-config: \(configString)" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// | ||
// ArtifactBundle.swift | ||
// | ||
// | ||
// Created by JP29872 on 2022/12/06. | ||
// | ||
|
||
import Foundation | ||
|
||
struct Variant: Encodable { | ||
var path: String | ||
var supportedTriples: [String] | ||
} | ||
|
||
struct Artifact: Encodable { | ||
enum `Type`: String, Encodable { | ||
case executable | ||
} | ||
|
||
var version: String | ||
var type: `Type` | ||
var variants: [Variant] | ||
} | ||
|
||
struct ArtifactBundle: Encodable { | ||
var schemaVersion: String | ||
var artifacts: [String: Artifact] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import Foundation | ||
|
||
public enum Config: String, RawRepresentable, Encodable { | ||
case debug | ||
case release | ||
|
||
var headUpperCase: String { | ||
switch self { | ||
case .debug: | ||
return "Debug" | ||
case .release: | ||
return "Release" | ||
} | ||
} | ||
} |
Oops, something went wrong.