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

Adding support for project additional files #314

Merged
Merged
11 changes: 4 additions & 7 deletions Sources/ProjectDescription/Project.swift
Expand Up @@ -6,19 +6,16 @@ public class Project: Codable {
public let name: String
public let targets: [Target]
public let settings: Settings?

public enum CodingKeys: String, CodingKey {
case name
case targets
case settings
}
public let additionalFiles: [WorkspaceElement]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still a workspace element?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we'd like to reuse the same type I'd change the name.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed - was looking for better names:

  • Reference, Element: too broad?
  • FileReference, FileElement: Folder references aren't files :/

Will think some more on suitable alternatives, if you had any please do share :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd stick to the Xcode project's convention, FileElement


public init(name: String,
settings: Settings? = nil,
targets: [Target] = []) {
targets: [Target] = [],
additionalFiles: [WorkspaceElement] = []) {
self.name = name
self.targets = targets
self.settings = settings
self.additionalFiles = additionalFiles
dumpIfNeeded(self)
}
}
65 changes: 2 additions & 63 deletions Sources/ProjectDescription/Workspace.swift
Expand Up @@ -10,7 +10,7 @@ public class Workspace: Codable {
public let projects: [String]

/// List of files to include in the workspace (e.g. Documentation)
public let additionalFiles: [Element]
public let additionalFiles: [WorkspaceElement]

/// Workspace
///
Expand All @@ -20,71 +20,10 @@ public class Workspace: Codable {
/// - name: Name of the workspace.
/// - projects: List of project relative paths (or glob patterns) to generate and include.
/// - additionalFiles: List of files to include in the workspace (e.g. Documentation)
public init(name: String, projects: [String], additionalFiles: [Element] = []) {
public init(name: String, projects: [String], additionalFiles: [WorkspaceElement] = []) {
self.name = name
self.projects = projects
self.additionalFiles = additionalFiles
dumpIfNeeded(self)
}
}

extension Workspace {
public enum Element: Codable {
/// A glob pattern of files to include
case glob(pattern: String)

/// Relative path to a directory to include
/// as a folder reference
case folderReference(path: String)

private enum TypeName: String, Codable {
case glob
case folderReference
}

private var typeName: TypeName {
switch self {
case .glob:
return .glob
case .folderReference:
return .folderReference
}
}

public enum CodingKeys: String, CodingKey {
case type
case pattern
case path
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(TypeName.self, forKey: .type)
switch type {
case .glob:
let pattern = try container.decode(String.self, forKey: .pattern)
self = .glob(pattern: pattern)
case .folderReference:
let path = try container.decode(String.self, forKey: .path)
self = .folderReference(path: path)
}
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(typeName, forKey: .type)
switch self {
case let .glob(pattern: pattern):
try container.encode(pattern, forKey: .pattern)
case let .folderReference(path: path):
try container.encode(path, forKey: .path)
}
}
}
}

extension Workspace.Element: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self = .glob(pattern: value)
}
}
67 changes: 67 additions & 0 deletions Sources/ProjectDescription/WorkspaceElement.swift
@@ -0,0 +1,67 @@
import Foundation

/// Workspace element
///
/// - glob: a glob pattern for files to include
/// - folderReference: a single path to a directory
///
/// Note: For convenience, an element can be represented as a string literal
/// `"some/pattern/**"` is the equivalent of `WorkspaceElement.glob(pattern: "some/pattern/**")`
public enum WorkspaceElement: Codable {
/// A glob pattern of files to include
case glob(pattern: String)

/// Relative path to a directory to include
/// as a folder reference
case folderReference(path: String)

private enum TypeName: String, Codable {
case glob
case folderReference
}

private var typeName: TypeName {
switch self {
case .glob:
return .glob
case .folderReference:
return .folderReference
}
}

public enum CodingKeys: String, CodingKey {
case type
case pattern
case path
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(TypeName.self, forKey: .type)
switch type {
case .glob:
let pattern = try container.decode(String.self, forKey: .pattern)
self = .glob(pattern: pattern)
case .folderReference:
let path = try container.decode(String.self, forKey: .path)
self = .folderReference(path: path)
}
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(typeName, forKey: .type)
switch self {
case let .glob(pattern: pattern):
try container.encode(pattern, forKey: .pattern)
case let .folderReference(path: path):
try container.encode(path, forKey: .path)
}
}
}

extension WorkspaceElement: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self = .glob(pattern: value)
}
}
2 changes: 1 addition & 1 deletion Sources/TuistGenerator/Generator.swift
Expand Up @@ -81,7 +81,7 @@ public class Generator: Generating {

let workspace = Workspace(name: project.name,
projects: graph.projectPaths,
additionalFiles: workspaceFiles.map(Workspace.Element.file))
additionalFiles: workspaceFiles.map(WorkspaceElement.file))

return try workspaceGenerator.generate(workspace: workspace,
path: path,
Expand Down
47 changes: 32 additions & 15 deletions Sources/TuistGenerator/Generator/ProjectFileElements.swift
Expand Up @@ -8,6 +8,13 @@ class ProjectFileElements {
struct FileElement: Hashable {
var path: AbsolutePath
var group: ProjectGroup
var isReference: Bool

init(path: AbsolutePath, group: ProjectGroup, isReference: Bool = false) {
self.path = path
self.group = group
self.isReference = isReference
}
}

// MARK: - Static
Expand Down Expand Up @@ -51,10 +58,7 @@ class ProjectFileElements {
files.formUnion(targetFileElements)
products.formUnion(targetProducts(target: target))
}
let projectFilePaths = projectFiles(project: project)
let projectFileElements = projectFilePaths.map {
FileElement(path: $0, group: project.filesGroup)
}
let projectFileElements = projectFiles(project: project)
files.formUnion(projectFileElements)

let pathsSort = filesSortener.sort
Expand Down Expand Up @@ -90,17 +94,27 @@ class ProjectFileElements {
filesGroup: project.filesGroup)
}

func projectFiles(project: Project) -> Set<AbsolutePath> {
var files = Set<AbsolutePath>()
func projectFiles(project: Project) -> Set<FileElement> {
var fileElements = Set<FileElement>()

/// Config files
if let debugConfigFile = project.settings?.debug?.xcconfig {
files.insert(debugConfigFile)
}
if let releaseConfigFile = project.settings?.release?.xcconfig {
files.insert(releaseConfigFile)
}
return files
let configFiles = [
project.settings?.debug?.xcconfig,
project.settings?.release?.xcconfig,
].compactMap { $0 }

fileElements.formUnion(configFiles.map {
FileElement(path: $0, group: project.filesGroup)
})

// Additional files
fileElements.formUnion(project.additionalFiles.map {
FileElement(path: $0.path,
group: project.filesGroup,
isReference: $0.isReference)
})

return fileElements
}

func targetProducts(target: Target) -> Set<String> {
Expand Down Expand Up @@ -239,6 +253,7 @@ class ProjectFileElements {
group = try groups.projectGroup(named: groupName)
}
guard let firstElement = addElement(relativePath: closestRelativeRelativePath,
isReference: fileElement.isReference,
from: sourceRootPath,
toGroup: group,
pbxproj: pbxproj) else {
Expand All @@ -256,6 +271,7 @@ class ProjectFileElements {
for component in fileElement.path.relative(to: lastPath).components {
if lastGroup == nil { return }
guard let element = addElement(relativePath: RelativePath(component),
isReference: fileElement.isReference,
from: lastPath,
toGroup: lastGroup!,
pbxproj: pbxproj) else {
Expand All @@ -269,6 +285,7 @@ class ProjectFileElements {
// MARK: - Internal

@discardableResult func addElement(relativePath: RelativePath,
isReference: Bool,
from: AbsolutePath,
toGroup: PBXGroup,
pbxproj: PBXProj) -> (element: PBXFileElement, path: AbsolutePath)? {
Expand Down Expand Up @@ -300,7 +317,7 @@ class ProjectFileElements {
name: name,
toGroup: toGroup,
pbxproj: pbxproj)
} else if isGroup(path: absolutePath) {
} else if isGroup(path: absolutePath), !isReference {
return addGroupElement(from: from,
folderAbsolutePath: absolutePath,
folderRelativePath: relativePath,
Expand Down Expand Up @@ -387,7 +404,7 @@ class ProjectFileElements {
name: String?,
toGroup: PBXGroup,
pbxproj: PBXProj) {
let lastKnownFileType = Xcode.filetype(extension: fileAbsolutePath.extension!)
let lastKnownFileType = fileAbsolutePath.extension.flatMap { Xcode.filetype(extension: $0) }
let file = PBXFileReference(sourceTree: .group, name: name, lastKnownFileType: lastKnownFileType, path: fileRelativePath.asString)
pbxproj.add(object: file)
toGroup.children.append(file)
Expand Down
Expand Up @@ -55,7 +55,7 @@ private class DirectoryStructure {
let fileHandler: FileHandling

let projects: [AbsolutePath]
let files: [Workspace.Element]
let files: [WorkspaceElement]

private let containers: [String] = [
".playground",
Expand All @@ -65,7 +65,7 @@ private class DirectoryStructure {
init(path: AbsolutePath,
fileHandler: FileHandling,
projects: [AbsolutePath],
files: [Workspace.Element]) {
files: [WorkspaceElement]) {
self.path = path
self.fileHandler = fileHandler
self.projects = projects
Expand Down Expand Up @@ -100,7 +100,7 @@ private class DirectoryStructure {
return root
}

private func fileNode(from element: Workspace.Element) -> Node {
private func fileNode(from element: WorkspaceElement) -> Node {
switch element {
case let .file(path: path):
return .file(path)
Expand All @@ -113,7 +113,7 @@ private class DirectoryStructure {
return .project(path)
}

private func isFileOrFolderReference(element: Workspace.Element) -> Bool {
private func isFileOrFolderReference(element: WorkspaceElement) -> Bool {
switch element {
case .folderReference:
return true
Expand Down
13 changes: 11 additions & 2 deletions Sources/TuistGenerator/Models/Project.swift
Expand Up @@ -20,24 +20,33 @@ public class Project: Equatable, CustomStringConvertible {
/// The group to place project files within
public let filesGroup: ProjectGroup

/// Additional files to include in the project
public let additionalFiles: [WorkspaceElement]

// MARK: - Init

/// Initializes the project with its attributes.
///
/// - Parameters:
/// - path: Path to the folder that contains the project manifest.
/// - name: Project name.
/// - targets: Project settings.
/// - settings: The settings to apply at the project level
/// - filesGroup: The root group to place project files within
/// - targets: The project targets
/// - additionalFiles: The additional files to include in the project
/// *(Those won't be included in any build phases)*
public init(path: AbsolutePath,
name: String,
settings: Settings? = nil,
filesGroup: ProjectGroup,
targets: [Target]) {
targets: [Target],
additionalFiles: [WorkspaceElement] = []) {
self.path = path
self.name = name
self.targets = targets
self.settings = settings
self.filesGroup = filesGroup
self.additionalFiles = additionalFiles
}

// MARK: - Init
Expand Down
20 changes: 2 additions & 18 deletions Sources/TuistGenerator/Models/Workspace.swift
Expand Up @@ -7,11 +7,11 @@ public class Workspace: Equatable {

public let name: String
public let projects: [AbsolutePath]
public let additionalFiles: [Element]
public let additionalFiles: [WorkspaceElement]

// MARK: - Init

public init(name: String, projects: [AbsolutePath], additionalFiles: [Element] = []) {
public init(name: String, projects: [AbsolutePath], additionalFiles: [WorkspaceElement] = []) {
self.name = name
self.projects = projects
self.additionalFiles = additionalFiles
Expand Down Expand Up @@ -43,19 +43,3 @@ extension Workspace {
additionalFiles: additionalFiles)
}
}

extension Workspace {
public enum Element: Equatable {
case file(path: AbsolutePath)
case folderReference(path: AbsolutePath)

var path: AbsolutePath {
switch self {
case let .file(path):
return path
case let .folderReference(path):
return path
}
}
}
}