polyhorn_cli/ios/xcodegen/
mod.rs

1//! Rust-wrapper around the third-party `xcodegen` utility (installed through
2//! Homebrew) that makes it possible to generate Xcode project files
3//! programmatically. Note: the documentation within this module is taken
4//! directly from the original excellent documentation of `xcodegen`.
5
6use serde::{Deserialize, Serialize};
7use std::collections::{HashMap, HashSet};
8
9/// Contains the `xcodegen` specification of a project.
10#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
11pub struct Project {
12    /// Name of the generated project.
13    pub name: String,
14
15    /// The list of targets in the project mapped by name.
16    pub targets: HashMap<String, Target>,
17}
18
19/// Represents a target that is built within a project.
20#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
21pub struct Target {
22    /// Product type of the target.
23    #[serde(rename = "type")]
24    pub product_type: ProductType,
25
26    /// Platform of the target.
27    pub platform: HashSet<Platform>,
28
29    /// The deployment target (e.g. `9.2`). If this is not specified the value
30    /// from the project set in `Options.deploymentTarget.PLATFORM` will be used.
31    #[serde(rename = "deploymentTarget")]
32    pub deployment_targets: HashMap<Platform, String>,
33
34    /// Source directories of the target.
35    pub sources: Vec<TargetSource>,
36
37    /// Target specific build settings. Default platform and product type
38    /// settings will be applied first before any custom settings defined here.
39    /// Other context dependant settings will be set automatically as well.
40    pub settings: HashMap<String, String>,
41
42    /// Dependencies for the target.
43    pub dependencies: Vec<Dependency>,
44}
45
46/// This will provide default build settings for a certain product type.
47#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
48pub enum ProductType {
49    /// Represents a regular application.
50    #[serde(rename = "application")]
51    Application,
52
53    /// Represents an App Clip.
54    #[serde(rename = "application-on-demand-install-capable")]
55    ApplicationOnDemandInstallCapable,
56}
57
58/// Indicates the platform that a target's product can run on.
59#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
60pub enum Platform {
61    /// Represents an application or extension that runs on iOS.
62    #[serde(rename = "iOS")]
63    IOS,
64
65    /// Represents an application or extension that runs on macOS.
66    #[serde(rename = "macOS")]
67    MacOS,
68
69    /// Represents an application or extension that runs on tvOS.
70    #[serde(rename = "tvOS")]
71    TVOS,
72
73    /// Represents an application or extension that runs on watchOS.
74    #[serde(rename = "watchOS")]
75    WatchOS,
76}
77
78/// Represents a source location that is included for a particular target within
79/// an Xcode project.
80#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
81pub struct TargetSource {
82    /// The path of the source file or directory.
83    pub path: String,
84}
85
86/// Represents a dependency of a target. Currently, only frameworks are
87/// supported. Note that framework dependencies are also used by `xcodegen` to
88/// refer to static libraries (despite the fact that frameworks are dynamic).
89#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
90#[serde(untagged)]
91pub enum Dependency {
92    /// Represents a framework dependency.
93    Framework {
94        /// Name or path of framework to link.
95        framework: String,
96
97        /// Whether or not to include a copy of the framework within the app's
98        /// bundle. This should be false for a static library because it's
99        /// unnecessary to include the `lib*.a` file in the bundle and
100        /// additionally, Apple might reject apps that do ship static libraries
101        /// that are not linked to the executable.
102        embed: bool,
103    },
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn test_spec() {
112        let project = Project {
113            name: "Example".to_owned(),
114            targets: vec![(
115                "Example".to_owned(),
116                Target {
117                    product_type: ProductType::Application,
118                    platform: vec![Platform::IOS].into_iter().collect(),
119                    deployment_targets: vec![(Platform::IOS, "8.0".to_owned())]
120                        .into_iter()
121                        .collect(),
122                    sources: vec![TargetSource {
123                        path: "Sources".to_owned(),
124                    }],
125                    settings: vec![
126                        (
127                            "LIBRARY_SEARCH_PATHS".to_owned(),
128                            "../x86_64-apple-ios/debug".to_owned(),
129                        ),
130                        ("OTHER_LDFLAGS".to_owned(), "-ObjC".to_owned()),
131                    ]
132                    .into_iter()
133                    .collect(),
134                    dependencies: vec![Dependency::Framework {
135                        framework: "libexample.a".to_owned(),
136                        embed: false,
137                    }],
138                },
139            )]
140            .into_iter()
141            .collect(),
142        };
143
144        let result = serde_yaml::to_string(&project).unwrap();
145        println!("Result: {}", result);
146    }
147}