diff --git a/.gitignore b/.gitignore index efaae4e5..b221605a 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ Packages/ Package.pins Package.resolved .build/ + +# Bazel +bazel-* diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 00000000..4374c872 --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,57 @@ +load( + "@build_bazel_rules_swift//swift:swift.bzl", + "swift_binary", + "swift_library", +) + +swift_library( + name = "NeedleFoundation", + srcs = glob( + ["Sources/NeedleFoundation/**/*.swift"], + allow_empty = False, + ), + module_name = "NeedleFoundation", + visibility = ["//visibility:public"], +) + +swift_binary( + name = "needle", + visibility = ["//visibility:public"], + deps = [ + ":NeedleLib", + ], +) + +swift_library( + name = "NeedleLib", + srcs = glob( + [ + "Generator/Sources/needle/**/*.swift", + ], + allow_empty = False, + ), + copts = ["-suppress-warnings"], + module_name = "Needle", + deps = [ + ":NeedleFramework", + "@SwiftToolsSupportCore//:TSCBasic", + "@UberSwiftCommon//:CommandFramework", + ], +) + +swift_library( + name = "NeedleFramework", + srcs = glob( + [ + "Generator/Sources/NeedleFramework/**/*.swift", + ], + allow_empty = False, + ), + copts = ["-suppress-warnings"], + module_name = "NeedleFramework", + deps = [ + "@SwiftSyntax//:SwiftParser_opt", + "@UberSwiftCommon//:SourceParsingFramework", + "@UberSwiftConcurrency//:Concurrency", + ], +) diff --git a/Generator/Sources/NeedleFramework/Parsing/BaseVisitor.swift b/Generator/Sources/NeedleFramework/Parsing/BaseVisitor.swift index 66304ff5..7bbab374 100644 --- a/Generator/Sources/NeedleFramework/Parsing/BaseVisitor.swift +++ b/Generator/Sources/NeedleFramework/Parsing/BaseVisitor.swift @@ -28,23 +28,23 @@ class BaseVisitor: SyntaxVisitor { var varNestingLevel = 0 let filePath: String - + init(filePath: String) { self.filePath = filePath super.init(viewMode: .sourceAccurate) } - + /// Whether we are parsing the line of code which declares a Component. /// We need this flag to determine if the generic argument we parse later is for the Component. var isParsingComponentDeclarationLine: Bool = false - + override func visitPost(_ node: FunctionCallExprSyntax) { if let callexpr = node.calledExpression.firstToken?.text, let currentEntityName = currentEntityNode?.typeName { componentToCallExprs[currentEntityName, default: []].insert(callexpr) } } - + override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind { varNestingLevel += 1 return .visitChildren @@ -53,10 +53,11 @@ class BaseVisitor: SyntaxVisitor { override func visitPost(_ node: VariableDeclSyntax) { defer { varNestingLevel -= 1 } guard let currentEntityName = currentEntityNode?.typeName, varNestingLevel == 1 else { return } + guard let entityNode = node as? EntityNode else { return } let isExtension = currentEntityNode is ExtensionDeclSyntax - let isPublic = node.isPublic || (isExtension && currentEntityNode?.isPublic == true) - let isPrivate = node.isPrivate || currentEntityNode?.isPrivate == true - let isFileprivate = node.isFileprivate || currentEntityNode?.isFileprivate == true + let isPublic = entityNode.isPublic || (isExtension && currentEntityNode?.isPublic == true) + let isPrivate = entityNode.isPrivate || currentEntityNode?.isPrivate == true + let isFileprivate = entityNode.isFileprivate || currentEntityNode?.isFileprivate == true let isInternal = !(isPublic || isPrivate || isFileprivate) let memberProperties = node.bindings.compactMap { pattern -> Property? in @@ -71,20 +72,24 @@ class BaseVisitor: SyntaxVisitor { return Property(name: propertyName, type: propertyType, isInternal: isInternal) } } - + propertiesDict[currentEntityName, default: []].append(contentsOf: memberProperties) } - + override func visitPost(_ node: ImportDeclSyntax) { - let importStatement = node.withoutTrivia().description.trimmed - imports.append(importStatement) + #if swift(>=5.9) + let importStatement = node.trimmed + #else + let importStatement = node.withoutTrivia() + #endif + imports.append(importStatement.description.trimmed) } - + override func visit(_ node: MemberDeclBlockSyntax) -> SyntaxVisitorContinueKind { isParsingComponentDeclarationLine = false return .visitChildren } - + override func visitPost(_ node: SourceFileSyntax) { if currentEntityNode == nil { imports = [] diff --git a/Generator/Sources/NeedleFramework/Parsing/Tasks/ASTProducerTask.swift b/Generator/Sources/NeedleFramework/Parsing/Tasks/ASTProducerTask.swift index e4b93b5a..0444369e 100644 --- a/Generator/Sources/NeedleFramework/Parsing/Tasks/ASTProducerTask.swift +++ b/Generator/Sources/NeedleFramework/Parsing/Tasks/ASTProducerTask.swift @@ -17,7 +17,9 @@ import Concurrency import Foundation import SourceParsingFramework -#if swift(>=5.6) +#if swift(>=5.9) +import SwiftParser +#elseif swift(>=5.6) import SwiftSyntaxParser #else import SwiftSyntax @@ -42,7 +44,11 @@ class ASTProducerTask: AbstractTask { /// - returns: The `AST` data model. /// - throws: Any error occurred during execution. override func execute() throws -> AST { + #if swift(>=5.9) + let syntax = try Parser.parse(source: sourceContent) + #else let syntax = try SyntaxParser.parse(sourceUrl) + #endif return AST(sourceHash: MD5(string: sourceContent), sourceFileSyntax: syntax, filePath: sourceUrl.path) } diff --git a/Generator/Sources/NeedleFramework/Utilities/SwiftSyntaxExtensions.swift b/Generator/Sources/NeedleFramework/Utilities/SwiftSyntaxExtensions.swift index 81913e16..13f81c0e 100644 --- a/Generator/Sources/NeedleFramework/Utilities/SwiftSyntaxExtensions.swift +++ b/Generator/Sources/NeedleFramework/Utilities/SwiftSyntaxExtensions.swift @@ -21,13 +21,17 @@ import SwiftSyntax /// An entity node is either a Protocol or Class syntax node protocol EntityNode: SyntaxNodeWithModifiers { var typeName: String { get } + #if swift(>=5.9) + var inheritanceClause: InheritanceClauseSyntax? { get } + #else var inheritanceClause: TypeInheritanceClauseSyntax? { get } + #endif } extension EntityNode { /// Checks whether the entity inherits from a certain type with `typeName` func inherits(from typeName: String) -> Bool { - + let inheritedTypeSyntax = inheritanceClause?.inheritedTypeCollection.first?.typeName // Usually, first token is the inherited type name. But sometimes it could also be the module prefix. // In that case, we need to look for the actual type name by checking for `MemberTypeIdentifierSyntax` @@ -37,7 +41,7 @@ extension EntityNode { return inheritedTypeSyntax?.as(MemberTypeIdentifierSyntax.self)?.name.text == typeName } } - + var inheritanceHasGenericArgument: Bool { let inheritanceTypeToken = inheritanceClause?.inheritedTypeCollection.first?.typeName return inheritanceTypeToken?.as(SimpleTypeIdentifierSyntax.self)?.genericArgumentClause != nil || @@ -46,24 +50,36 @@ extension EntityNode { } protocol SyntaxNodeWithModifiers { + #if swift(>=5.9) + var modifiers: DeclModifierListSyntax { get } + #else var modifiers: ModifierListSyntax? { get } + #endif } extension SyntaxNodeWithModifiers { + var modifiersACLDeclaration: String? { + #if swift(>=5.9) + return modifiers.first?.name.text + #else + return modifiers?.first?.name.text + #endif + } + var isInternal: Bool { - modifiers?.first?.name.text == nil || modifiers?.first?.name.text == "internal" + modifiersACLDeclaration == nil || modifiersACLDeclaration == "internal" } var isPublic: Bool { - modifiers?.first?.name.text == "public" + modifiersACLDeclaration == "public" } var isPrivate: Bool { - modifiers?.first?.name.text == "private" + modifiersACLDeclaration == "private" } var isFileprivate: Bool { - modifiers?.first?.name.text == "fileprivate" + modifiersACLDeclaration == "fileprivate" } } @@ -115,5 +131,3 @@ extension ExtensionDeclSyntax: EntityNode { return extendedType.description.trimmed } } - -extension VariableDeclSyntax: SyntaxNodeWithModifiers {} diff --git a/Generator/Tests/NeedleFrameworkTests/Parsing/ASTProducerTaskTests.swift b/Generator/Tests/NeedleFrameworkTests/Parsing/ASTProducerTaskTests.swift index 321658ac..defcf9a0 100644 --- a/Generator/Tests/NeedleFrameworkTests/Parsing/ASTProducerTaskTests.swift +++ b/Generator/Tests/NeedleFrameworkTests/Parsing/ASTProducerTaskTests.swift @@ -16,7 +16,9 @@ import XCTest @testable import NeedleFramework -#if swift(>=5.6) +#if swift(>=5.9) +import SwiftParser +#elseif swift(>=5.6) import SwiftSyntaxParser #else import SwiftSyntax diff --git a/Generator/Tests/NeedleFrameworkTests/Parsing/DeclarationsParserTaskTests.swift b/Generator/Tests/NeedleFrameworkTests/Parsing/DeclarationsParserTaskTests.swift index b97af6e5..dc6367f6 100644 --- a/Generator/Tests/NeedleFrameworkTests/Parsing/DeclarationsParserTaskTests.swift +++ b/Generator/Tests/NeedleFrameworkTests/Parsing/DeclarationsParserTaskTests.swift @@ -17,7 +17,9 @@ import XCTest @testable import NeedleFramework @testable import SourceParsingFramework -#if swift(>=5.6) +#if swift(>=5.9) +import SwiftParser +#elseif swift(>=5.6) import SwiftSyntaxParser #else import SwiftSyntax @@ -47,12 +49,12 @@ class DeclarationsParserTaskTests: AbstractParserTests { let imports = ["import UIKit", "import RIBs", "import Foundation", "import protocol Audio.Recordable"] let ast = AST(sourceHash: MD5(string: sourceContent), sourceFileSyntax: try! SyntaxParser.parse(sourceUrl), filePath: sourceUrl.path) - + let task = DeclarationsParserTask(ast: ast) let node = try! task.execute() XCTAssertEqual(node.components.count, 3) - + // Imports XCTAssertEqual(node.imports, imports) diff --git a/Generator/Tests/NeedleFrameworkTests/Parsing/Pluginized/PluginizedDeclarationsParserTaskTests.swift b/Generator/Tests/NeedleFrameworkTests/Parsing/Pluginized/PluginizedDeclarationsParserTaskTests.swift index bebb2379..277a50cb 100644 --- a/Generator/Tests/NeedleFrameworkTests/Parsing/Pluginized/PluginizedDeclarationsParserTaskTests.swift +++ b/Generator/Tests/NeedleFrameworkTests/Parsing/Pluginized/PluginizedDeclarationsParserTaskTests.swift @@ -16,7 +16,9 @@ import XCTest @testable import NeedleFramework -#if swift(>=5.6) +#if swift(>=5.9) +import SwiftParser +#elseif swift(>=5.6) import SwiftSyntaxParser #else import SwiftSyntax @@ -33,7 +35,7 @@ class PluginizedDeclarationsParserTaskTests: AbstractParserTests { let task = PluginizedDeclarationsParserTask(ast: ast) let node = try! task.execute() - + // Imports for statement in imports { XCTAssertTrue(node.imports.contains(statement)) diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 00000000..b8611255 --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,255 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "build_bazel_rules_apple", + sha256 = "34c41bfb59cdaea29ac2df5a2fa79e5add609c71bb303b2ebb10985f93fa20e7", + url = "https://github.com/bazelbuild/rules_apple/releases/download/3.1.1/rules_apple.3.1.1.tar.gz", +) + +load( + "@build_bazel_rules_apple//apple:repositories.bzl", + "apple_rules_dependencies", +) + +apple_rules_dependencies() + +load( + "@build_bazel_rules_swift//swift:repositories.bzl", + "swift_rules_dependencies", +) + +swift_rules_dependencies() + +load( + "@build_bazel_rules_swift//swift:extras.bzl", + "swift_rules_extra_dependencies", +) + +swift_rules_extra_dependencies() + +load( + "@build_bazel_apple_support//lib:repositories.bzl", + "apple_support_dependencies", +) + +apple_support_dependencies() + +SWIFTSYTNAX_VERSION = "509.0.0" + +http_archive( + name = "SwiftSyntax", + sha256 = "1cddda9f7d249612e3d75d4caa8fd9534c0621b8a890a7d7524a4689bce644f1", + strip_prefix = "swift-syntax-%s" % SWIFTSYTNAX_VERSION, + url = "https://github.com/apple/swift-syntax/archive/refs/tags/%s.tar.gz" % SWIFTSYTNAX_VERSION, +) + +SWIFT_TOOLS_SUPPORT_CORE_VERSION = "0.5.1" + +http_archive( + name = "SwiftToolsSupportCore", + build_file_content = """ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") +objc_library( + name = "TSCclibc", + srcs = glob( + [ + "Sources/TSCclibc/*.c", + ], + allow_empty = False, + ), + hdrs = [ + "Sources/TSCclibc/include/TSCclibc.h", + "Sources/TSCclibc/include/indexstore_functions.h", + "Sources/TSCclibc/include/process.h", + ], + module_name = "TSCclibc", +) + +swift_library( + name = "TSCLibc", + srcs = glob( + [ + "Sources/TSCLibc/**/*.swift", + ], + allow_empty = False, + ), + module_name = "TSCLibc", + deps = [ + ":TSCclibc", + ], +) + +swift_library( + name = "TSCBasic", + srcs = glob( + [ + "Sources/TSCBasic/**/*.swift", + ], + allow_empty = False, + ), + copts = [ + "-suppress-warnings", + ], + module_name = "TSCBasic", + visibility = ["//visibility:public"], + deps = [ + ":TSCLibc", + "@SwiftSystem//:SystemPackage" + ], +) + +swift_library( + name = "TSCUtility", + srcs = glob( + [ + "Sources/TSCUtility/**/*.swift", + ], + allow_empty = False, + ), + copts = [ + "-suppress-warnings", + ], + module_name = "TSCUtility", + visibility = ["//visibility:public"], + deps = [ + ":TSCBasic", + ], +) + """, + sha256 = "85ab60d84827ffa01233a766b0d36c6a7aacb0dba0e6304e83ebd1f359504d4d", + strip_prefix = "swift-tools-support-core-%s" % SWIFT_TOOLS_SUPPORT_CORE_VERSION, + urls = ["https://github.com/apple/swift-tools-support-core/archive/refs/tags/%s.tar.gz" % SWIFT_TOOLS_SUPPORT_CORE_VERSION], +) + +UBER_SWIFT_COMMON_VERSION = "0.5.0" + +http_archive( + name = "UberSwiftCommon", + build_file_content = """ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "CommandFramework", + srcs = glob( + [ + "Sources/CommandFramework/**/*.swift", + ], + allow_empty = False, + ), + copts = ["-suppress-warnings"], + module_name = "CommandFramework", + visibility = ["//visibility:public"], + deps = [ + ":SourceParsingFramework", + "@SwiftToolsSupportCore//:TSCUtility", + ], +) + +swift_library( + name = "SourceParsingFramework", + srcs = glob( + [ + "Sources/SourceParsingFramework/**/*.swift", + ], + allow_empty = False, + ), + copts = ["-suppress-warnings"], + module_name = "SourceParsingFramework", + visibility = ["//visibility:public"], + deps = [ + "@SwiftToolsSupportCore//:TSCBasic", + "@UberSwiftConcurrency//:Concurrency", + ], +) + """, + sha256 = "f4df1d64ee99e7df43079bd49da5fb331d7c4f9556d4852d072e146bce0a7e7e", + strip_prefix = "swift-common-%s" % UBER_SWIFT_COMMON_VERSION, + urls = ["https://github.com/uber/swift-common/archive/refs/tags/v%s.tar.gz" % UBER_SWIFT_COMMON_VERSION], +) + +UBER_SWIFT_CONCURRENCY_VERSION = "0.7.1" + +http_archive( + name = "UberSwiftConcurrency", + build_file_content = """ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( +name = "Concurrency", +srcs = glob( + [ + "Sources/Concurrency/**/*.swift", + ], + allow_empty = False, +), +copts = ["-suppress-warnings"], +module_name = "Concurrency", +visibility = ["//visibility:public"], +deps = [ + ":ObjCBridges", +], +) + +objc_library( +name = "ObjCBridges", +srcs = glob( + [ + "Sources/ObjCBridges/*.m", + ], + allow_empty = False, +), +hdrs = glob( + [ + "Sources/ObjCBridges/include/*.h", + ], + allow_empty = False, +), +includes = ["Sources/ObjCBridges/include"], +module_name = "ObjCBridges", +) + """, + sha256 = "8f95c4e2d4b98f38f14adb53120372d36b7fb54a65ddda1a8f2df3e3ae069ab3", + strip_prefix = "swift-concurrency-%s" % UBER_SWIFT_CONCURRENCY_VERSION, + urls = ["https://github.com/uber/swift-concurrency/archive/refs/tags/v%s.tar.gz" % UBER_SWIFT_CONCURRENCY_VERSION], +) + +SWIFT_SYSTEM_VERSION = "1.2.1" + +http_archive( + name = "SwiftSystem", + build_file_content = """ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +cc_library( + name = "CSystem", + srcs = glob( + ["Sources/CSystem/**/*.c"], + allow_empty = False + ), + hdrs = glob( + ["Sources/CSystem/include/*.h"], + allow_empty = False + ), +) + +swift_library( + name = "SystemPackage", + module_name = "SystemPackage", + srcs = glob( + ["Sources/System/**/*.swift"], + allow_empty = False + ), + defines = [ + "SYSTEM_PACKAGE", + ], + deps = [ + ":CSystem", + ], + visibility = ["@SwiftToolsSupportCore//:__subpackages__"], +) + + """, + sha256 = "ab771be8a944893f95eed901be0a81a72ef97add6caa3d0981e61b9b903a987d", + strip_prefix = "swift-system-%s" % SWIFT_SYSTEM_VERSION, + url = "https://github.com/apple/swift-system/archive/refs/tags/%s.tar.gz" % SWIFT_SYSTEM_VERSION, +)