Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GSOC-Week22 #2

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 34 additions & 7 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,30 +1,51 @@
// swift-tools-version:5.10
// swift-tools-version:5.9

import PackageDescription

let package = Package(
name: "swift-aws-lambda-sam-dsl",
platforms: [
.macOS(.v14),
.macOS(.v12),
],
products: [
// SwiftPM plugin to deploy a SAM Lambda function
.plugin(name: "AWSLambdaDeployer", targets: ["AWSLambdaDeployer"]),


.executable(name: "DeploymentDescriptorGeneratorExecutable",
targets: ["DeploymentDescriptorGeneratorExecutable"]),

// SwiftPM plugin to generate a SAM deployment descriptor
.plugin(name: "AWSLambdaDescriptorGenerator", targets: ["AWSLambdaDescriptorGenerator"]),

// Shared Library to generate a SAM deployment descriptor
.library(name: "AWSLambdaDeploymentDescriptor", type: .dynamic, targets: ["AWSLambdaDeploymentDescriptor"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-log.git", .upToNextMajor(from: "1.4.2")),
.package(url: "https://github.com/hummingbird-project/hummingbird-mustache.git", from: "1.0.3"),
],
targets: [
.target(
name: "AWSLambdaDeploymentDescriptor",
path: "Sources/AWSLambdaDeploymentDescriptor"
),
// SAM Deployment Descriptor Generator
.target(
name: "AWSLambdaDeploymentDescriptorGenerator",
path: "Sources/AWSLambdaDeploymentDescriptorGenerator"
.executableTarget(
name: "DeploymentDescriptorGeneratorExecutable",
dependencies: [
.byName(name: "AWSLambdaDeploymentDescriptorGenerator"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "Logging", package: "swift-log")
]
),
.target(name: "AWSLambdaDeploymentDescriptorGenerator", dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "HummingbirdMustache", package: "hummingbird-mustache"),
],
resources: [
.process("Resources/SamTranslatorSchema.json")
]
),
.plugin(
name: "AWSLambdaDeployer",
Expand All @@ -36,6 +57,12 @@ let package = Package(
// permissions: [.writeToPackageDirectory(reason: "This plugin generates a SAM template to describe your deployment")]
)
),

.plugin(
name: "AWSLambdaDescriptorGenerator",
capability: .buildTool(),
dependencies: ["DeploymentDescriptorGeneratorExecutable"]
),
.testTarget(
name: "AWSLambdaDeploymentDescriptorTests",
dependencies: [
Expand Down
29 changes: 29 additions & 0 deletions Plugins/AWSLambdaDescriptorGenerator/Plugin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

import Foundation
import PackagePlugin

@main struct AWSLambdaDescriptorGenerator: BuildToolPlugin {
func createBuildCommands(
context: PluginContext,
target: Target
) throws -> [Command] {

guard let target = target as? SourceModuleTarget else {
return []
}

return try target.sourceFiles(withSuffix: "json").map { samSchema in
let base = samSchema.path.stem
let input = samSchema.path
let output = context.pluginWorkDirectory.appending(["\(base).swift"])

return .buildCommand(displayName: "Generating constants for \(base)",
executable: try context.tool(named: "DeploymentDescriptorGeneratorExecutable").path,
arguments: [input.string, output.string],
inputFiles: [input],
outputFiles: [output])
}

}
}

130 changes: 130 additions & 0 deletions Plugins/AWSLambdaDescriptorGenerator/PluginUtils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// ===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2022 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
// ===----------------------------------------------------------------------===//

import Dispatch
import Foundation
import PackagePlugin

struct Utils {
@discardableResult
static func execute(
executable: Path,
arguments: [String],
customWorkingDirectory: Path? = .none,
logLevel: ProcessLogLevel
) throws -> String {
if logLevel >= .debug {
print("\(executable.string) \(arguments.joined(separator: " "))")
}

var output = ""
let outputSync = DispatchGroup()
let outputQueue = DispatchQueue(label: "AWSLambdaPackager.output")
let outputHandler = { (data: Data?) in
dispatchPrecondition(condition: .onQueue(outputQueue))

outputSync.enter()
defer { outputSync.leave() }

guard let _output = data.flatMap({ String(data: $0, encoding: .utf8)?.trimmingCharacters(in: CharacterSet(["\n"])) }), !_output.isEmpty else {
return
}

output += _output + "\n"

switch logLevel {
case .silent:
break
case .debug(let outputIndent), .output(let outputIndent):
print(String(repeating: " ", count: outputIndent), terminator: "")
print(_output)
fflush(stdout)
}
}

let pipe = Pipe()
pipe.fileHandleForReading.readabilityHandler = { fileHandle in outputQueue.async { outputHandler(fileHandle.availableData) } }

let process = Process()
process.standardOutput = pipe
process.standardError = pipe
process.executableURL = URL(fileURLWithPath: executable.string)
process.arguments = arguments
if let workingDirectory = customWorkingDirectory {
process.currentDirectoryURL = URL(fileURLWithPath: workingDirectory.string)
}
process.terminationHandler = { _ in
outputQueue.async {
outputHandler(try? pipe.fileHandleForReading.readToEnd())
}
}

try process.run()
process.waitUntilExit()

// wait for output to be full processed
outputSync.wait()

if process.terminationStatus != 0 {
// print output on failure and if not already printed
if logLevel < .output {
print(output)
fflush(stdout)
}
throw ProcessError.processFailed([executable.string] + arguments, process.terminationStatus, output)
}

return output
}

enum ProcessError: Error, CustomStringConvertible {
case processFailed([String], Int32, String)

var description: String {
switch self {
case .processFailed(let arguments, let code, _):
return "\(arguments.joined(separator: " ")) failed with code \(code)"
}
}
}
enum ProcessLogLevel: Comparable {
case silent
case output(outputIndent: Int)
case debug(outputIndent: Int)

var naturalOrder: Int {
switch self {
case .silent:
return 0
case .output:
return 1
case .debug:
return 2
}
}

static var output: Self {
.output(outputIndent: 2)
}

static var debug: Self {
.debug(outputIndent: 2)
}

static func < (lhs: ProcessLogLevel, rhs: ProcessLogLevel) -> Bool {
lhs.naturalOrder < rhs.naturalOrder
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,128 @@ TODO
1. read `samtranslator schema.json`
2. generate `../DeploymentDescriptor.swift`

*/
*/
import Foundation
import HummingbirdMustache
import Logging


public protocol DeploymentDescriptorGeneratorCommand {
var inputFile: String? { get }
var configFile: String? { get }
var prefix: String? { get }
var outputFolder: String { get }
var inputFolder: String? { get }
var endpoints: String { get }
var module: String? { get }
var output: Bool { get }
var logLevel: String? { get }
}

public struct DeploymentDescriptorGenerator {
struct FileError: Error {
let filename: String
let error: Error
}

let command: DeploymentDescriptorGeneratorCommand
// let library: HBMustacheLibrary
let logger: Logging.Logger

public init(command: DeploymentDescriptorGeneratorCommand) throws {
self.command = command
// self.library = try Templates.createLibrary()
var logger = Logging.Logger(label: "DeploymentDescriptorGenerator")
logger.logLevel = self.command.logLevel.map { Logging.Logger.Level(rawValue: $0) ?? .info } ?? .info
self.logger = logger
}

public func generate() {

// generate code here

let filePath = Bundle.module.path(forResource: "SamTranslatorSchema", ofType: "json") ?? ""
let url = URL(fileURLWithPath: filePath)

do {
let schemaData = try Data(contentsOf: url)
do {
let schema = try analyzeSAMSchema(from: schemaData)
// You can now access and analyze the schema information stored in the `schema` struct
} catch {
print("Error analyzing schema: \(error)")
}

} catch {
print("Error getting schemaData contents of URL: \(error)")
}


}

func analyzeSAMSchema(from jsonData: Data) throws -> JSONSchema {
let decoder = JSONDecoder()
let schema = try decoder.decode(JSONSchema.self, from: jsonData)

print("Schema Information:")
print(" - Schema URL: \(schema.schema)")
print(" - Overall Type: \(schema.type)")

if let properties = schema.properties {
print("\n Properties:")
for (name, propertyType) in properties {
print(" - \(name): \(propertyType)") // Briefly describe the type
}
}

if let definitions = schema.definitions {
print("\n Definitions:")
for (name, definitionType) in definitions {
print(" - \(name): \(definitionType)") // Briefly describe the type
}
}

return schema
}



func getModelFiles() -> [String] {
if let input = self.command.inputFile {
return [input]
} else if let inputFolder = self.command.inputFolder {
if let module = command.module {
return []
// return Glob.entries(pattern: "\(inputFolder)/\(module)*.json")
}
return []
// return Glob.entries(pattern: "\(inputFolder)/*.json")
} else {
return []
}
}




}


extension String {
/// Only writes to file if the string contents are different to the file contents. This is used to stop XCode rebuilding and reindexing files unnecessarily.
/// If the file is written to XCode assumes it has changed even when it hasn't
/// - Parameters:
/// - toFile: Filename
/// - atomically: make file write atomic
/// - encoding: string encoding
func writeIfChanged(toFile: String) throws -> Bool {
do {
let original = try String(contentsOfFile: toFile)
guard original != self else { return false }
} catch {
// print(error)
}
try write(toFile: toFile, atomically: true, encoding: .utf8)
return true
}
}
Loading