Fixing header paths including folders and non-header files (#356)

Resolves https://github.com/tuist/tuist/issues/352

- Specifying a permissive glob pattern (e.g. `Headers/**`) for headers would incorrectly include all files and folders as headers
- Limiting the paths to header files resolves this issue
- Adding Objective-C classes to test out headers within the fixtures

Test Plan:

- Verify unit tests pass via `swift build`
- Verify acceptance tests pass via `bundle exec rake features`
- Manually generate `fixtures/ios_app_with_frameworks` via `tuist generate`
- Verify `Framework2` has the appropriate public, private & project headers set
This commit is contained in:
Kas 2019-05-17 21:24:29 +01:00 committed by GitHub
parent d7e7c2f9f5
commit 47118ec48f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 135 additions and 24 deletions

View File

@ -21,6 +21,7 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
- Updating init template to avoid warnings https://github.com/tuist/tuist/pull/339 by @kwridan
- Fixing generation failures due to asset catalog & `**/*.png` glob patterns handling https://github.com/tuist/tuist/pull/346 by @kwridan
- Supporting bundle target dependencies that reside in different projects (in `TuistGenerator`) https://github.com/tuist/tuist/pull/348 by @kwridan
- Fixing header paths including folders and non-header files https://github.com/tuist/tuist/pull/356 by @kwridan
## 0.14.0

View File

@ -5,6 +5,8 @@ import XcodeProj
/// Headers
public class Headers: Equatable {
public static let extensions = Xcode.headersExtensions
// MARK: - Attributes
public let `public`: [AbsolutePath]

View File

@ -156,7 +156,7 @@ extension TuistGenerator.Project {
}
let schemes = manifest.schemes.map { TuistGenerator.Scheme.from(manifest: $0) }
let additionalFiles = manifest.additionalFiles.flatMap {
TuistGenerator.FileElement.from(manifest: $0,
path: path,
@ -296,11 +296,22 @@ extension TuistGenerator.CoreDataModel {
extension TuistGenerator.Headers {
static func from(manifest: ProjectDescription.Headers, path: AbsolutePath, fileHandler: FileHandling) -> TuistGenerator.Headers {
let `public` = manifest.public.map { fileHandler.glob(path, glob: $0) } ?? []
let `private` = manifest.private.map { fileHandler.glob(path, glob: $0) } ?? []
let project = manifest.project.map { fileHandler.glob(path, glob: $0) } ?? []
let `public` = manifest.public.map { headerFiles(path: path, glob: $0, fileHandler: fileHandler) } ?? []
let `private` = manifest.private.map { headerFiles(path: path, glob: $0, fileHandler: fileHandler) } ?? []
let project = manifest.project.map { headerFiles(path: path, glob: $0, fileHandler: fileHandler) } ?? []
return Headers(public: `public`, private: `private`, project: project)
}
private static func headerFiles(path: AbsolutePath,
glob: String,
fileHandler: FileHandling) -> [AbsolutePath] {
return fileHandler.glob(path, glob: glob).filter {
if let `extension` = $0.extension, Headers.extensions.contains(".\(`extension`)") {
return true
}
return false
}
}
}
extension TuistGenerator.Dependency {

View File

@ -348,34 +348,45 @@ class GeneratorModelLoaderTest: XCTestCase {
func test_headers() throws {
// Given
let publicPath = path.appending(component: "public")
try fileHandler.createFolder(publicPath)
try fileHandler.touch(publicPath.appending(component: "a.h"))
try fileHandler.touch(publicPath.appending(component: "b.h"))
try fileHandler.createFiles([
"Sources/public/A1.h",
"Sources/public/A1.m",
"Sources/public/A2.h",
"Sources/public/A2.m",
let projectPath = path.appending(component: "project")
try fileHandler.createFolder(projectPath)
try fileHandler.touch(projectPath.appending(component: "c.h"))
"Sources/private/B1.h",
"Sources/private/B1.m",
"Sources/private/B2.h",
"Sources/private/B2.m",
let privatePath = path.appending(component: "private")
try fileHandler.createFolder(privatePath)
"Sources/project/C1.h",
"Sources/project/C1.m",
"Sources/project/C2.h",
"Sources/project/C2.m",
])
let manifest = HeadersManifest(public: "public/*.h", private: "private/*.h", project: "project/*.h")
let manifest = HeadersManifest(public: "Sources/public/**",
private: "Sources/private/**",
project: "Sources/project/**")
// When
let model = TuistGenerator.Headers.from(manifest: manifest, path: path, fileHandler: fileHandler)
// Then
XCTAssertEqual(model.public, [
publicPath.appending(component: "a.h"),
publicPath.appending(component: "b.h"),
])
"Sources/public/A1.h",
"Sources/public/A2.h",
].map { fileHandler.currentPath.appending(RelativePath($0)) })
XCTAssertEqual(model.private, [
"Sources/private/B1.h",
"Sources/private/B2.h",
].map { fileHandler.currentPath.appending(RelativePath($0)) })
XCTAssertEqual(model.project, [
projectPath.appending(component: "c.h"),
])
XCTAssertEqual(model.private, [])
"Sources/project/C1.h",
"Sources/project/C2.h",
].map { fileHandler.currentPath.appending(RelativePath($0)) })
}
func test_coreDataModel() throws {

View File

@ -114,9 +114,9 @@ Each target in the list of project targets can be initialized with the following
- **[String]:** A list of files or list of glob patterns _(e.g. `["Sources/**"]`)_
- **Resources (optional):** List of [FileElement](#FileElement)s to include in the resource build phase.
- **Headers (optional):** Target headers. It accepts a `Header` type that is initialized with the following attributes:
- **Public:** Relative path to the folder that contains the public headers.
- **Private:** Relative path to the folder that contains the private headers.
- **Project:** Relative path to the folder that contains the project headers.
- **Public:** Relative path or glob pattern to the public headers _(e.g. `Sources/Public/**`)_
- **Private:** Relative path or glob pattern to the private headers _(e.g. `Sources/Private/**`)_
- **Project:** Relative path or glob pattern to the project headers _(e.g. `Sources/Project/**`)_
- **Entitlements (optional):** Relative path to the entitlements file.
- **Actions (optional):** Target actions allow defining extra script build phases. It's an array of `TargetAction` objects that that can be of type `pre` and `post`:
- **pre:** Executed before the target-specific build phases.

View File

@ -9,11 +9,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidFinishLaunching(_: UIApplication) {
let framework1 = Framework1File()
let framework2 = Framework2File()
let framework2Objc = MyPublicClass()
print(hello())
print("AppDelegate -> \(framework1.hello())")
print("AppDelegate -> \(framework1.helloFromFramework2())")
print("AppDelegate -> \(framework2.hello())")
print("AppDelegate -> \(framework2Objc.hello())")
}
func hello() -> String {

View File

@ -8,6 +8,9 @@ let project = Project(name: "Framework2",
bundleId: "io.tuist.Framework2",
infoPlist: "Config/Framework2-Info.plist",
sources: "Sources/**",
headers: Headers(public: "Sources/Public/**",
private: "Sources/Private/**",
project: "Sources/Project/**"),
dependencies: []),
Target(name: "Framework2Tests",

View File

@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MyPrivateClass : NSObject
- (NSString *)hello;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,11 @@
#import "MyPrivateClass.h"
@implementation MyPrivateClass
- (NSString *)hello
{
return @"MyPrivateClass.hello";
}
@end

View File

@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MyProjectClass : NSObject
- (NSString *)hello;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,11 @@
#import "MyProjectClass.h"
@implementation MyProjectClass
- (NSString *)hello
{
return @"MyProjectClass.hello";
}
@end

View File

@ -0,0 +1,2 @@
#import <Framework2/MyPublicClass.h>

View File

@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MyPublicClass : NSObject
- (NSString *)hello;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,11 @@
#import "MyPublicClass.h"
@implementation MyPublicClass
- (NSString *)hello
{
return @"MyPublicClass.hello";
}
@end

View File

@ -0,0 +1,10 @@
import XCTest
@testable import Framework2
class MyPublicClassTests: XCTestCase {
func testHello() {
let sut = MyPublicClass()
XCTAssertEqual("MyPublicClass.hello", sut.hello())
}
}