diff --git a/PluginExamples/Package.swift b/PluginExamples/Package.swift index 31d062239..92a6fa13b 100644 --- a/PluginExamples/Package.swift +++ b/PluginExamples/Package.swift @@ -47,6 +47,15 @@ let package = Package( .plugin(name: "SwiftProtobufPlugin", package: "swift-protobuf") ] ), + .target( + name: "CustomProtoPath", + dependencies: [ + .product(name: "SwiftProtobuf", package: "swift-protobuf") + ], + plugins: [ + .plugin(name: "SwiftProtobufPlugin", package: "swift-protobuf") + ] + ), .testTarget( name: "ExampleTests", dependencies: [ @@ -54,6 +63,7 @@ let package = Package( .target(name: "Nested"), .target(name: "Import"), .target(name: "AccessLevelOnImport"), + .target(name: "CustomProtoPath"), ] ), ], diff --git a/PluginExamples/Sources/CustomProtoPath/empty.swift b/PluginExamples/Sources/CustomProtoPath/empty.swift new file mode 100644 index 000000000..d4445d2a5 --- /dev/null +++ b/PluginExamples/Sources/CustomProtoPath/empty.swift @@ -0,0 +1,3 @@ +/// DO NOT DELETE. +/// +/// We need to keep this file otherwise the plugin is not running. diff --git a/PluginExamples/Sources/CustomProtoPath/protos/Bar/Bar.proto b/PluginExamples/Sources/CustomProtoPath/protos/Bar/Bar.proto new file mode 100644 index 000000000..8e672a88c --- /dev/null +++ b/PluginExamples/Sources/CustomProtoPath/protos/Bar/Bar.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message Bar { + string name = 1; +} diff --git a/PluginExamples/Sources/CustomProtoPath/protos/Foo/Foo.proto b/PluginExamples/Sources/CustomProtoPath/protos/Foo/Foo.proto new file mode 100644 index 000000000..c7dbff913 --- /dev/null +++ b/PluginExamples/Sources/CustomProtoPath/protos/Foo/Foo.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +import "Bar/Bar.proto"; + +message Foo { + Bar bar = 1; +} diff --git a/PluginExamples/Sources/CustomProtoPath/protos/Main.proto b/PluginExamples/Sources/CustomProtoPath/protos/Main.proto new file mode 100644 index 000000000..18b17a89a --- /dev/null +++ b/PluginExamples/Sources/CustomProtoPath/protos/Main.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +import "Bar/Bar.proto"; +import "Foo/Foo.proto"; + +message Main { + Bar bar = 1; + Foo foo = 2; +} \ No newline at end of file diff --git a/PluginExamples/Sources/CustomProtoPath/swift-protobuf-config.json b/PluginExamples/Sources/CustomProtoPath/swift-protobuf-config.json new file mode 100644 index 000000000..24722c38d --- /dev/null +++ b/PluginExamples/Sources/CustomProtoPath/swift-protobuf-config.json @@ -0,0 +1,13 @@ +{ + "invocations": [ + { + "protoFiles": [ + "Bar/Bar.proto", + "Foo/Foo.proto", + "Main.proto" + ], + "visibility": "public", + "protoPath": "protos" + } + ] +} diff --git a/PluginExamples/Sources/ExampleTests/ExampleTests.swift b/PluginExamples/Sources/ExampleTests/ExampleTests.swift index 1bd2d5354..9f3905fa4 100644 --- a/PluginExamples/Sources/ExampleTests/ExampleTests.swift +++ b/PluginExamples/Sources/ExampleTests/ExampleTests.swift @@ -1,3 +1,4 @@ +import CustomProtoPath import Import import Nested import Simple @@ -15,7 +16,16 @@ final class ExampleTests: XCTestCase { } func testImport() { - let foo = Foo.with { $0.bar = .with { $0.name = "Bar" } } + let foo = Import.Foo.with { $0.bar = .with { $0.name = "Bar" } } XCTAssertEqual(foo.bar.name, "Bar") } + + func testCustomProtoPath() { + let main = CustomProtoPath.Main.with { + $0.bar = .with { $0.name = "Bar" } + $0.foo = .with { $0.bar = .with { $0.name = "BarInFoo" } } + } + XCTAssertEqual(main.bar.name, "Bar") + XCTAssertEqual(main.foo.bar.name, "BarInFoo") + } } diff --git a/Plugins/SwiftProtobufPlugin/plugin.swift b/Plugins/SwiftProtobufPlugin/plugin.swift index 74af4b966..faef5800e 100644 --- a/Plugins/SwiftProtobufPlugin/plugin.swift +++ b/Plugins/SwiftProtobufPlugin/plugin.swift @@ -77,6 +77,9 @@ struct SwiftProtobufPlugin { } /// An array of paths to `.proto` files for this invocation. + /// + /// If the `protoPath` parameter is specified, the files must be specified + /// relative to that directory. Otherwise, relative to the target source directory. var protoFiles: [String] /// The visibility of the generated files. var visibility: Visibility? @@ -86,6 +89,15 @@ struct SwiftProtobufPlugin { var implementationOnlyImports: Bool? /// Whether import statements should be preceded with visibility. var useAccessLevelOnImports: Bool? + /// Overrides the base directory used to find protobuf files. + /// + /// This must be specified as a path relative to the target source directory. + /// For example, if you are storing the protofiles at `MyLibrary/Sources/MyLib/protos`, + /// you should specify `protos` as the value for this parameter. + /// + /// If you have multiple subdirectories you wish to include, + /// you should specify multiple `invocations` instead. + var protoPath: String? } /// The path to the `protoc` binary. @@ -171,10 +183,14 @@ struct SwiftProtobufPlugin { "--swift_out=\(outputDirectory)", ] - // We need to add the target directory as a search path since we require the user to specify - // the proto files relative to it. + let protoDirectory = if let protoPath = invocation.protoPath { + directory.appending(protoPath) + } else { + directory + } + protocArgs.append("-I") - protocArgs.append("\(directory)") + protocArgs.append("\(protoDirectory)") // Add the visibility if it was set if let visibility = invocation.visibility { @@ -202,7 +218,7 @@ struct SwiftProtobufPlugin { for var file in invocation.protoFiles { // Append the file to the protoc args so that it is used for generating protocArgs.append("\(file)") - inputFiles.append(directory.appending(file)) + inputFiles.append(protoDirectory.appending(file)) // The name of the output file is based on the name of the input file. // We validated in the beginning that every file has the suffix of .proto