├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── Dangerfile ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── bin └── framework-generate ├── docs ├── FrameworkSpec └── FrameworkSpec.md ├── framework-generate.gemspec └── lib ├── SampleFrameworkSpec ├── framework-generate.rb └── framework-generate ├── copy-carthage-frameworks.sh ├── language.rb ├── platform.rb ├── project.rb ├── script.rb └── target.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | /coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /spec/examples.txt 9 | /test/tmp/ 10 | /test/version_tmp/ 11 | /tmp/ 12 | 13 | # Used by dotenv library to load environment variables. 14 | # .env 15 | 16 | ## Specific to RubyMotion: 17 | .dat* 18 | .repl_history 19 | build/ 20 | *.bridgesupport 21 | build-iPhoneOS/ 22 | build-iPhoneSimulator/ 23 | 24 | ## Specific to RubyMotion (use of CocoaPods): 25 | # 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 29 | # 30 | # vendor/Pods/ 31 | 32 | ## Documentation cache and generated files: 33 | /.yardoc/ 34 | /_yardoc/ 35 | /doc/ 36 | /rdoc/ 37 | 38 | ## Environment normalization: 39 | /.bundle/ 40 | /vendor/bundle 41 | /lib/bundler/man/ 42 | 43 | # for a library or gem, you might want to ignore these files since the code is 44 | # intended to run in multiple environments; otherwise, check them in: 45 | # Gemfile.lock 46 | # .ruby-version 47 | # .ruby-gemset 48 | 49 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 50 | .rvmrc 51 | .idea 52 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | cache: 3 | directories: 4 | - bundle 5 | 6 | rvm: 7 | - 2.4.0 8 | - 2.3.3 9 | - 2.1.9 10 | - 2.0 11 | 12 | bundler_args: "--without documentation --path bundle" 13 | 14 | before_install: 15 | - gem install bundler 16 | 17 | script: 18 | - bundle exec danger 19 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of 4 | fostering an open and welcoming community, we pledge to respect all people who 5 | contribute through reporting issues, posting feature requests, updating 6 | documentation, submitting pull requests or patches, and other activities. 7 | 8 | We are committed to making participation in this project a harassment-free 9 | experience for everyone, regardless of level of experience, gender, gender 10 | identity and expression, sexual orientation, disability, personal appearance, 11 | body size, race, ethnicity, age, religion, or nationality. 12 | 13 | Examples of unacceptable behavior by participants include: 14 | 15 | * The use of sexualized language or imagery 16 | * Personal attacks 17 | * Trolling or insulting/derogatory comments 18 | * Public or private harassment 19 | * Publishing other's private information, such as physical or electronic 20 | addresses, without explicit permission 21 | * Other unethical or unprofessional conduct 22 | 23 | Project maintainers have the right and responsibility to remove, edit, or 24 | reject comments, commits, code, wiki edits, issues, and other contributions 25 | that are not aligned to this Code of Conduct, or to ban temporarily or 26 | permanently any contributor for other behaviors that they deem inappropriate, 27 | threatening, offensive, or harmful. 28 | 29 | By adopting this Code of Conduct, project maintainers commit themselves to 30 | fairly and consistently applying these principles to every aspect of managing 31 | this project. Project maintainers who do not follow or enforce the Code of 32 | Conduct may be permanently removed from the project team. 33 | 34 | This code of conduct applies both within project spaces and in public spaces 35 | when an individual is representing the project or its community. 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 38 | reported by contacting a project maintainer at [pierremarcairoldi@gmail.com](mailto:pierremarcairoldi@gmail.com). All 39 | complaints will be reviewed and investigated and will result in a response that 40 | is deemed necessary and appropriate to the circumstances. Maintainers are 41 | obligated to maintain confidentiality with regard to the reporter of an 42 | incident. 43 | 44 | 45 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 46 | version 1.3.0, available at 47 | [http://contributor-covenant.org/version/1/3/0/][version] 48 | 49 | [homepage]: http://contributor-covenant.org 50 | [version]: http://contributor-covenant.org/version/1/3/0/ -------------------------------------------------------------------------------- /Dangerfile: -------------------------------------------------------------------------------- 1 | # Sometimes it's a README fix, or something like that - which isn't relevant for 2 | # including in a project's CHANGELOG for example 3 | declared_trivial = github.pr_title.include? "#trivial" 4 | 5 | # Make it more obvious that a PR is a work in progress and shouldn't be merged yet 6 | warn("PR is classed as Work in Progress") if github.pr_title.include? "[WIP]" 7 | 8 | # Warn when there is a big PR 9 | warn("Big PR") if git.lines_of_code > 500 10 | 11 | # Don't let testing shortcuts get into master by accident 12 | fail("fdescribe left in tests") if `grep -r fdescribe specs/ `.length > 1 13 | fail("fit left in tests") if `grep -r fit specs/ `.length > 1 14 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | source 'https://rubygems.org' 3 | 4 | gem 'framework-generate', path: '.' 5 | gem 'danger' 6 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | framework-generate (1.3.2) 5 | xcodeproj (>= 1.5.0, < 2.0.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | CFPropertyList (2.3.6) 11 | addressable (2.5.1) 12 | public_suffix (~> 2.0, >= 2.0.2) 13 | atomos (0.1.2) 14 | claide (1.0.2) 15 | claide-plugins (0.9.2) 16 | cork 17 | nap 18 | open4 (~> 1.3) 19 | colored2 (3.1.2) 20 | cork (0.3.0) 21 | colored2 (~> 3.1) 22 | danger (5.3.3) 23 | claide (~> 1.0) 24 | claide-plugins (>= 0.9.2) 25 | colored2 (~> 3.1) 26 | cork (~> 0.1) 27 | faraday (~> 0.9) 28 | faraday-http-cache (~> 1.0) 29 | git (~> 1) 30 | kramdown (~> 1.5) 31 | octokit (~> 4.7) 32 | terminal-table (~> 1) 33 | faraday (0.12.2) 34 | multipart-post (>= 1.2, < 3) 35 | faraday-http-cache (1.3.1) 36 | faraday (~> 0.8) 37 | git (1.3.0) 38 | kramdown (1.14.0) 39 | multipart-post (2.0.0) 40 | nanaimo (0.2.3) 41 | nap (1.1.0) 42 | octokit (4.7.0) 43 | sawyer (~> 0.8.0, >= 0.5.3) 44 | open4 (1.3.4) 45 | public_suffix (2.0.5) 46 | sawyer (0.8.1) 47 | addressable (>= 2.3.5, < 2.6) 48 | faraday (~> 0.8, < 1.0) 49 | terminal-table (1.8.0) 50 | unicode-display_width (~> 1.1, >= 1.1.1) 51 | unicode-display_width (1.3.0) 52 | xcodeproj (1.5.6) 53 | CFPropertyList (~> 2.3.3) 54 | atomos (~> 0.1.2) 55 | claide (>= 1.0.2, < 2.0) 56 | colored2 (~> 3.1) 57 | nanaimo (~> 0.2.3) 58 | 59 | PLATFORMS 60 | ruby 61 | 62 | DEPENDENCIES 63 | danger 64 | framework-generate! 65 | 66 | BUNDLED WITH 67 | 1.15.4 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Pierre-Marc Airoldi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # framework-generate 2 | [![Gem Version](https://badge.fury.io/rb/framework-generate.svg)](https://badge.fury.io/rb/framework-generate) 3 | [![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](LICENSE) 4 | 5 | Simple tool to help generate a [multiplatform, single-scheme Xcode project](http://promisekit.org/news/2016/08/Multiplatform-Single-Scheme-Xcode-Projects/). 6 | 7 | ## Installation 8 | 9 | Add this line to your Gemfile: 10 | 11 | ```rb 12 | gem 'framework-generate' 13 | ``` 14 | 15 | ## Usage 16 | 17 | Once installed you can run the generate command from the command line as follows 18 | 19 | ```bash 20 | framework-generate 21 | ``` 22 | 23 | `framework-generate` will look for a `FrameworkSpec` file in the current folder to generate your Xcode project. An example of a `FrameworkSpec` can be found in the [`docs`](docs/FrameworkSpec) folder of this repoisitory. 24 | 25 | To view the full `FrameworkSpec` documentation see the [`docs`](docs/FrameworkSpec.md) folder. 26 | 27 | ### Sample FrameworkSpec Files 28 | 29 | Don't know where to start? Some FrameworkSpec files exist [here](https://github.com/petester42/framework-specs) for a couple projects. It can be a good place to start! 30 | 31 | ## Contributing 32 | 33 | Love framework-generate but want to make it even better? 34 | 35 | Open source isn't just writing code. We could use your help with any of the 36 | following: 37 | 38 | - Finding (and reporting!) bugs. 39 | - New feature suggestions. 40 | - Answering questions on issues. 41 | - Documentation improvements. 42 | - Reviewing pull requests. 43 | - Helping to manage issue priorities. 44 | - Fixing bugs/new features. 45 | 46 | If any of that sounds cool to you, send a pull request! After a few 47 | contributions, we'll add you as an admin to the repo so you can merge pull 48 | requests and help build framework-generate. 49 | 50 | Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by [its terms](CODE_OF_CONDUCT.md). 51 | 52 | ### How to contribute 53 | 54 | Contributing is easy but you will need to have bundler installed. If you don't it can be installed using: 55 | 56 | ```bash 57 | [sudo] gem install bundler 58 | ``` 59 | 60 | When you have bundler installed, simply make your changes to the tool and then try it out using: 61 | 62 | ```bash 63 | bundle install 64 | bundle exec framework-generate 65 | ``` 66 | 67 | Once your change is good to go open up a pull request! 68 | 69 | ## License 70 | 71 | This project is licensed under the terms of the MIT license. See the [LICENSE](LICENSE) file. 72 | -------------------------------------------------------------------------------- /bin/framework-generate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'framework-generate' 4 | 5 | FrameworkGenerate::Runner.generate 6 | -------------------------------------------------------------------------------- /docs/FrameworkSpec: -------------------------------------------------------------------------------- 1 | language = swift("3.0") 2 | platforms = [ 3 | macos("10.11"), 4 | ios("8.0"), 5 | tvos("9.0"), 6 | watchos("2.0") 7 | ] 8 | 9 | moya_tests = new_target do |target| 10 | target.name = "MoyaTests" 11 | target.platforms = platforms 12 | target.language = language 13 | target.info_plist = "Sources/Supporting Files/Info.plist" 14 | target.bundle_id = "com.cocoapods.MoyaTests" 15 | target.include_files = ["Tests/**/*.swift"] 16 | target.exclude_files = [] 17 | target.resource_files = ["Tests/**/*.png"] 18 | target.dependencies = [ 19 | "Moya", 20 | "ReactiveMoya", 21 | "RxMoya", 22 | "Nimble", 23 | "Quick", 24 | "OHHTTPStubs", 25 | "Result", 26 | "Alamofire", 27 | "ReactiveSwift", 28 | "RxSwift" 29 | ] 30 | target.type = :unit_test_bundle 31 | target.enable_code_coverage = true 32 | end 33 | 34 | moya = new_target do |target| 35 | target.name = "Moya" 36 | target.platforms = platforms 37 | target.language = language 38 | target.info_plist = "Sources/Supporting Files/Info.plist" 39 | target.bundle_id = "com.cocoapods.Moya" 40 | target.header = "Sources/Supporting Files/Moya.h" 41 | target.include_files = ["Sources/Moya/**/*.swift"] 42 | target.exclude_files = [] 43 | target.is_safe_for_extensions = false 44 | target.test_target = moya_tests 45 | end 46 | 47 | reactive_moya = new_target do |target| 48 | target.name = "ReactiveMoya" 49 | target.platforms = platforms 50 | target.language = language 51 | target.info_plist = "Sources/Supporting Files/Info.plist" 52 | target.bundle_id = "com.cocoapods.ReactiveMoya" 53 | target.header = "Sources/Supporting Files/ReactiveMoya.h" 54 | target.include_files = ["Sources/ReactiveMoya/**/*.swift"] 55 | target.exclude_files = [] 56 | target.is_safe_for_extensions = false 57 | target.dependencies = ["Moya"] 58 | target.test_target = moya_tests 59 | end 60 | 61 | rx_moya = new_target do |target| 62 | target.name = "RxMoya" 63 | target.platforms = platforms 64 | target.language = language 65 | target.info_plist = "Sources/Supporting Files/Info.plist" 66 | target.bundle_id = "com.cocoapods.RxMoya" 67 | target.header = "Sources/Supporting Files/RxMoya.h" 68 | target.include_files = ["Sources/RxMoya/**/*.swift"] 69 | target.exclude_files = [] 70 | target.is_safe_for_extensions = false 71 | target.dependencies = ["Moya"] 72 | target.test_target = moya_tests 73 | end 74 | 75 | project.name = "Moya" 76 | project.scripts_directory = "scripts" 77 | project.targets = [ 78 | moya, 79 | reactive_moya, 80 | rx_moya, 81 | moya_tests 82 | ] 83 | -------------------------------------------------------------------------------- /docs/FrameworkSpec.md: -------------------------------------------------------------------------------- 1 | # FrameworkSpec Syntax Reference 2 | 3 | The `FrameworkSpec` is a file that describes how to create a project file for a [multiplatform, single-scheme Xcode project](http://promisekit.org/news/2016/08/Multiplatform-Single-Scheme-Xcode-Projects/). 4 | 5 | ## Project 6 | 7 | A project contains all the information to generate a project file. A `FrameworkSpec` automatically creates a project so its properties can be accessed directly. All properties need to be set in order for the project to be generated properly. 8 | 9 | ### Properties 10 | 11 | #### Name 12 | 13 | The name that will be for the project file. 14 | 15 | ```ruby 16 | project.name = "Project" 17 | ``` 18 | 19 | #### Targets 20 | 21 | An array of [targets](#target) that the framework will create. 22 | 23 | ```ruby 24 | target = new_target do |target| 25 | target.name = "Target" 26 | target.info_plist = "Sources/Supporting Files/Info.plist" 27 | target.bundle_id = "com.org.target" 28 | target.header = "Sources/Supporting Files/Target.h" 29 | target.include_files = ["Sources/**/*.swift"] 30 | target.language = swift("3.0") 31 | target.platforms = [ 32 | macos("10.11"), 33 | ios("8.0"), 34 | tvos("9.0"), 35 | watchos("2.0") 36 | ] 37 | end 38 | 39 | project.targets = [target] 40 | ``` 41 | 42 | #### Scripts Directory 43 | 44 | The directory to save scripts to. 45 | 46 | ```ruby 47 | project.scripts_directory = "scripts" 48 | ``` 49 | 50 | ## Target 51 | 52 | A target contains the information that is needed to generate an xcode target inside of an Xcode project. 53 | 54 | ### Properties 55 | 56 | #### Name 57 | 58 | The name of the target that will be generated. 59 | 60 | ```ruby 61 | target = new_target do |target| 62 | target.name = "Target" 63 | end 64 | ``` 65 | #### Language 66 | 67 | The [language](#language-1) that the target will support. 68 | 69 | ```ruby 70 | target = new_target do |target| 71 | target.language = swift("3.0") 72 | end 73 | ``` 74 | 75 | #### Platforms 76 | 77 | An array of [platforms](#platform) that the target will support. 78 | 79 | ```ruby 80 | target = new_target do |target| 81 | target.platforms = [ 82 | macos("10.11"), 83 | ios("8.0"), 84 | tvos("9.0"), 85 | watchos("2.0") 86 | ] 87 | end 88 | ``` 89 | 90 | #### Info Plist 91 | 92 | The path to the target's `info.plist` file. 93 | 94 | ```ruby 95 | target = new_target do |target| 96 | target.info_plist = "Sources/Supporting Files/Info.plist" 97 | end 98 | ``` 99 | 100 | #### Bundle Id 101 | 102 | The target's bundle id. 103 | 104 | ```ruby 105 | target = new_target do |target| 106 | target.bundle_id = "com.org.target" 107 | end 108 | ``` 109 | 110 | #### Header 111 | 112 | The target's framework header. 113 | 114 | ```ruby 115 | target = new_target do |target| 116 | target.header = "Sources/Supporting Files/Target.h" 117 | end 118 | ``` 119 | 120 | #### Include Files 121 | 122 | An array for the source files to be included in the target. Patterns can be used to automatically find all files matching the pattern. 123 | 124 | ```ruby 125 | target = new_target do |target| 126 | target.include_files = ["Sources/**/*.swift", "ThirdParty/**/*.swift"] 127 | end 128 | ``` 129 | 130 | #### Exclude Files 131 | 132 | An array for the source files to be excluded from the target. Patterns can be used to automatically find all files matching the pattern. 133 | 134 | ```ruby 135 | target = new_target do |target| 136 | target.exclude_files = ["Sources/**/*_old.swift", "ThirdParty/**/*_old.swift"] 137 | end 138 | ``` 139 | 140 | #### Resource Files 141 | 142 | An array for the resouce files to be included from the target. Patterns can be used to automatically find all files matching the pattern. 143 | 144 | ```ruby 145 | target = new_target do |target| 146 | target.resource_files = ["Images/**/*.png"] 147 | end 148 | ``` 149 | 150 | #### Dependencies 151 | 152 | An array for the dependencies a target has. If a dependency's name matches the name of another target in the `FrameworkSpec` then it will be used. Otherwise all dependencies are assumed to come from the carthage build folder. 153 | 154 | ```ruby 155 | dependency = new_target do |target| 156 | target.name = "Dependency" 157 | end 158 | 159 | target = new_target do |target| 160 | target.dependencies = ["Dependency", "Alamofire"] 161 | end 162 | ``` 163 | 164 | #### Type 165 | 166 | The type of target to generate. The default value is `:framework` and the possible values include: `:framework` and `:unit_test_bundle`. 167 | 168 | ```ruby 169 | target = new_target do |target| 170 | target.type = :framework 171 | end 172 | ``` 173 | 174 | #### Pre Build Scripts 175 | 176 | An array of [scripts](#script) to run before building the target builds. 177 | 178 | ```ruby 179 | hello_world = new_script do |script| 180 | script.name = "Hello World" 181 | script.script = 'echo "hello world"' 182 | end 183 | 184 | target = new_target do |target| 185 | target.pre_build_scripts = [ 186 | hello_world 187 | ] 188 | end 189 | ``` 190 | 191 | #### Post Build Scripts 192 | 193 | An array of [scripts](#script) to run after building the target. 194 | 195 | ```ruby 196 | hello_world = new_script do |script| 197 | script.name = "Hello World" 198 | script.script = 'echo "hello world"' 199 | end 200 | 201 | target = new_target do |target| 202 | target.post_build_scripts = [ 203 | hello_world 204 | ] 205 | end 206 | ``` 207 | 208 | #### Test Target 209 | 210 | Linking a test bundle to a target to be able to run the test action in Xcode. 211 | 212 | ```ruby 213 | test_target = new_target do |target| 214 | target.type = :unit_test_bundle 215 | end 216 | 217 | target = new_target do |target| 218 | target.test_target = test_target 219 | end 220 | ``` 221 | 222 | #### Is Safe For Extensions 223 | 224 | Set whether the target uses only extension safe apis. The default value is `false` and the possible values include: `true` and `false`. 225 | 226 | ```ruby 227 | target = new_target do |target| 228 | target.is_safe_for_extensions = true 229 | end 230 | ``` 231 | 232 | #### Enable Code Coverage 233 | 234 | Set whether the target should generate code coverage data when tests are run. The default value is `false` and the possible values include: `true` and `false`. 235 | 236 | ```ruby 237 | target = new_target do |target| 238 | target.enable_code_coverage = true 239 | end 240 | ``` 241 | 242 | #### Launch Arguments 243 | 244 | An array of arguements to pass to the target when it is lauched 245 | 246 | ```ruby 247 | target = new_target do |target| 248 | target.launch_arguments = [ 249 | "-NSShowNonLocalizedStrings YES" 250 | ] 251 | end 252 | ``` 253 | 254 | #### Environment Variables 255 | 256 | An array of environment variables to pass to the target when it is lauched 257 | 258 | ```ruby 259 | target = new_target do |target| 260 | target.environment_variables = [ 261 | "NSZombieEnabled": "YES" 262 | ] 263 | end 264 | ``` 265 | 266 | ## Platform 267 | 268 | Describes the platform constraints the framework will target. Syntactic sugar for the platform can also be used for the platform like follows: 269 | 270 | ```ruby 271 | macos("10.11") 272 | ios("8.0") 273 | tvos("9.0") 274 | watchos("2.0") 275 | ``` 276 | 277 | ### Properties 278 | 279 | #### Type 280 | 281 | The type of the platform to target. The possible values are: `:macos`, `:ios`, `:tvos` and `watchos`. 282 | 283 | ```ruby 284 | macos = new_platform do |platform| 285 | platform.type = :macos 286 | end 287 | ``` 288 | 289 | #### Minimum Version 290 | 291 | The minimun deployment version of the platform for the target. 292 | 293 | ```ruby 294 | macos = new_platform do |platform| 295 | platform.minimum_version = "8.0" 296 | end 297 | ``` 298 | 299 | #### Search Paths 300 | 301 | Sets the framework search paths for the project. The default value is setup to use the carthage build folder. 302 | 303 | ```ruby 304 | macos = new_platform do |platform| 305 | platform.search_paths = "$(SRCROOT)/Carthage/Build/Mac/ $(inherited)" 306 | end 307 | ``` 308 | 309 | ## Language 310 | 311 | Describes the language constraints the framework will target. Syntactic sugar for the platform can also be used for the platform like follows: 312 | 313 | ```ruby 314 | swift "3.0" 315 | objc 316 | ``` 317 | 318 | ### Properties 319 | 320 | #### Type 321 | 322 | The type of the platform to target. The possible values are: `:swift`, and `objc`. 323 | 324 | ```ruby 325 | swift = new_language do |language| 326 | language.type = :swift 327 | end 328 | ``` 329 | 330 | #### Version 331 | 332 | Sets the version of the language to use. This setting only applies to the `:swift` type. 333 | 334 | ```ruby 335 | swift = new_language do |language| 336 | language.version = "3.0" 337 | end 338 | ``` 339 | 340 | ## Script 341 | 342 | Describes scripts that can be added to a target. 343 | 344 | ### Properties 345 | 346 | #### Name 347 | 348 | The name of the script that will be shown in the target's build phases in the Xcode project. 349 | 350 | ```ruby 351 | hello_world = new_script do |script| 352 | script.name = "Hello World" 353 | end 354 | ``` 355 | 356 | #### Script 357 | 358 | The contents of the script that will be executed. 359 | 360 | ```ruby 361 | hello_world = new_script do |script| 362 | script.script = "echo 'hello world'" 363 | end 364 | ``` 365 | 366 | #### Inputs 367 | 368 | An array of inputs to pass to the executed script. 369 | 370 | ```ruby 371 | hello_world = new_script do |script| 372 | script.inputs = ["hello", "world"] 373 | end 374 | ``` 375 | -------------------------------------------------------------------------------- /framework-generate.gemspec: -------------------------------------------------------------------------------- 1 | VERSION = '1.3.2'.freeze 2 | 3 | Gem::Specification.new do |s| 4 | s.name = 'framework-generate' 5 | s.version = VERSION 6 | s.summary = 'Tool to generate an Xcode framework project' 7 | s.description = 'Simple tool to help generate a multiplatform, single-scheme Xcode project' 8 | s.authors = ['Pierre-Marc Airoldi'] 9 | s.email = ['pierremarcairoldi@gmail.com'] 10 | s.homepage = 'https://pierremarcairoldi.com' 11 | s.license = 'MIT' 12 | 13 | s.required_ruby_version = '>= 2.0.0' 14 | 15 | s.files = %w(README.md LICENSE) + Dir['lib/**/*.rb'] + Dir['lib/**/*.sh'] + Dir['lib/SampleFrameworkSpec'] 16 | s.executables = %w(framework-generate) 17 | s.require_paths = %w(lib) 18 | 19 | s.add_dependency 'xcodeproj', '>= 1.5.0', '< 2.0.0' 20 | end 21 | -------------------------------------------------------------------------------- /lib/SampleFrameworkSpec: -------------------------------------------------------------------------------- 1 | target = new_target do |target| 2 | target.name = #TARGET_NAME# 3 | target.language = swift("3.0") 4 | target.platforms = [ 5 | macos("10.11"), 6 | ios("8.0"), 7 | tvos("9.0"), 8 | watchos("2.0") 9 | ] 10 | target.info_plist = #TARGET_INFO_PLIST# 11 | target.bundle_id = #TARGET_BUNDLE_ID# 12 | target.include_files = [ 13 | #TARGET_INCLUDE_FILES# 14 | ] 15 | end 16 | 17 | project.name = #PROJECT_NAME# 18 | project.targets = [ 19 | target 20 | ] 21 | -------------------------------------------------------------------------------- /lib/framework-generate.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | 3 | module FrameworkGenerate 4 | class Runner 5 | def self.generate 6 | file_path = "#{Dir.pwd}/FrameworkSpec" 7 | 8 | unless File.exist?(file_path) 9 | puts "Couldn't find FrameworkSpec. Do you want to create one? [Y/N]" 10 | create_file = gets.chomp 11 | 12 | if create_file.casecmp('Y').zero? 13 | sample_framework_spec = File.join(File.dirname(__FILE__), 'SampleFrameworkSpec') 14 | 15 | FileUtils.cp(sample_framework_spec, file_path) 16 | 17 | abort 'Created a FrameworkSpec. Update the contents of the FrameworkSpec file and rerun the command' 18 | elsif 19 | abort 'Cannot create a project without a FrameworkSpec' 20 | end 21 | end 22 | 23 | file_contents = File.read(file_path) 24 | 25 | project = FrameworkGenerate::Project.new 26 | project.instance_eval(file_contents, file_path) 27 | 28 | project.generate 29 | end 30 | end 31 | 32 | autoload :Project, 'framework-generate/project' 33 | autoload :Language, 'framework-generate/language' 34 | autoload :Platform, 'framework-generate/platform' 35 | autoload :Target, 'framework-generate/target' 36 | autoload :Script, 'framework-generate/script' 37 | end 38 | -------------------------------------------------------------------------------- /lib/framework-generate/copy-carthage-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | case "$PLATFORM_NAME" in 4 | macosx) plat=Mac;; 5 | iphone*) plat=iOS;; 6 | watch*) plat=watchOS;; 7 | appletv*) plat=tvOS;; 8 | *) echo "error: Unknown PLATFORM_NAME: $PLATFORM_NAME"; exit 1;; 9 | esac 10 | 11 | for (( n = 0; n < SCRIPT_INPUT_FILE_COUNT; n++ )); do 12 | VAR=SCRIPT_INPUT_FILE_$n 13 | framework=$(basename "${!VAR}") 14 | export SCRIPT_INPUT_FILE_$n="$SRCROOT"/Carthage/Build/$plat/"$framework" 15 | done 16 | 17 | /usr/local/bin/carthage copy-frameworks || exit 18 | 19 | for (( n = 0; n < SCRIPT_INPUT_FILE_COUNT; n++ )); do 20 | VAR=SCRIPT_INPUT_FILE_$n 21 | source=${!VAR}.dSYM 22 | dest=${BUILT_PRODUCTS_DIR}/$(basename "$source") 23 | ditto "$source" "$dest" || exit 24 | done -------------------------------------------------------------------------------- /lib/framework-generate/language.rb: -------------------------------------------------------------------------------- 1 | module FrameworkGenerate 2 | class Language 3 | attr_accessor :type, :version 4 | 5 | def initialize(type = nil, version = nil) 6 | @type = type 7 | @version = version 8 | 9 | yield(self) if block_given? 10 | end 11 | 12 | def to_s 13 | "Language<#{type}, #{version}>" 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/framework-generate/platform.rb: -------------------------------------------------------------------------------- 1 | module FrameworkGenerate 2 | class Platform 3 | attr_accessor :type, :minimum_version, :search_paths 4 | 5 | def initialize(type = nil, minimum_version = nil, search_paths = nil) 6 | @type = type 7 | @minimum_version = minimum_version 8 | @search_paths = search_paths.nil? ? default_search_paths : search_paths 9 | 10 | yield(self) if block_given? 11 | end 12 | 13 | def to_s 14 | "Platform<#{type}, #{minimum_version}, #{search_paths}>" 15 | end 16 | 17 | def self.supported_platforms(platforms, is_test_target = false) 18 | platforms 19 | .reject { |platform| is_test_target && platform.type == :watchos } 20 | .map(&:raw_values) 21 | .join(' ') 22 | end 23 | 24 | def self.find_platform(platforms, type) 25 | platforms.find { |platform| platform.type == type } 26 | end 27 | 28 | def self.deployment_target(platform) 29 | platform.minimum_version 30 | end 31 | 32 | def self.search_paths(platform) 33 | platform.search_paths 34 | end 35 | 36 | def raw_values 37 | case @type 38 | when :macos 39 | 'macosx' 40 | when :ios 41 | 'iphoneos iphonesimulator' 42 | when :tvos 43 | 'appletvos appletvsimulator' 44 | when :watchos 45 | 'watchos watchsimulator' 46 | else 47 | abort 'platform not supported!' 48 | end 49 | end 50 | 51 | def default_search_paths 52 | case @type 53 | when :macos 54 | '$(SRCROOT)/Carthage/Build/Mac/ $(inherited)' 55 | when :ios 56 | '$(SRCROOT)/Carthage/Build/iOS/ $(inherited)' 57 | when :tvos 58 | '$(SRCROOT)/Carthage/Build/tvOS/ $(inherited)' 59 | when :watchos 60 | '$(SRCROOT)/Carthage/Build/watchOS/ $(inherited)' 61 | else 62 | abort 'platform not supported!' 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/framework-generate/project.rb: -------------------------------------------------------------------------------- 1 | require 'xcodeproj' 2 | 3 | module FrameworkGenerate 4 | class Project 5 | attr_accessor :name, :targets, :scripts_directory 6 | 7 | def initialize(name = nil, targets = nil, scripts_directory = nil) 8 | @name = name 9 | @targets = targets 10 | @scripts_directory = scripts_directory 11 | 12 | yield(self) if block_given? 13 | end 14 | 15 | def project 16 | yield(self) if block_given? 17 | self 18 | end 19 | 20 | def to_s 21 | "Project<#{name}, #{targets}>" 22 | end 23 | 24 | # DSL 25 | def new_target 26 | Target.new do |target| 27 | yield(target) 28 | end 29 | end 30 | 31 | def new_platform 32 | Platform.new do |platform| 33 | yield(platform) 34 | end 35 | end 36 | 37 | def new_language 38 | Language.new do |language| 39 | yield(language) 40 | end 41 | end 42 | 43 | def new_script 44 | Script.new do |script| 45 | yield(script) 46 | end 47 | end 48 | 49 | # sugar 50 | def macos(version, search_paths = nil) 51 | Platform.new(:macos, version, search_paths) 52 | end 53 | 54 | def ios(version, search_paths = nil) 55 | Platform.new(:ios, version, search_paths) 56 | end 57 | 58 | def tvos(version, search_paths = nil) 59 | Platform.new(:tvos, version, search_paths) 60 | end 61 | 62 | def watchos(version, search_paths = nil) 63 | Platform.new(:watchos, version, search_paths) 64 | end 65 | 66 | def swift(version) 67 | Language.new(:swift, version) 68 | end 69 | 70 | def objc 71 | Language.new(:objc, nil) 72 | end 73 | 74 | # Interface 75 | def project_path 76 | return @name if File.extname(@name) == '.xcodeproj' 77 | 78 | "#{@name}.xcodeproj" 79 | end 80 | 81 | def general_build_settings(settings) 82 | settings['SDKROOT'] = 'macosx' 83 | settings['TARGETED_DEVICE_FAMILY'] = '1,2,3,4' 84 | settings['CODE_SIGN_IDENTITY'] = '' 85 | settings['COMBINE_HIDPI_IMAGES'] = 'YES' 86 | settings['SWIFT_OPTIMIZATION_LEVEL'] = '-Owholemodule' 87 | settings['CLANG_WARN_INFINITE_RECURSION'] = 'YES' 88 | settings['CLANG_WARN_SUSPICIOUS_MOVE'] = 'YES' 89 | settings['ENABLE_STRICT_OBJC_MSGSEND'] = 'YES' 90 | settings['GCC_NO_COMMON_BLOCKS'] = 'YES' 91 | settings['CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING'] = 'YES' 92 | settings['CLANG_WARN_COMMA'] = 'YES' 93 | settings['CLANG_WARN_NON_LITERAL_NULL_CONVERSION'] = 'YES' 94 | settings['CLANG_WARN_OBJC_LITERAL_CONVERSION'] = 'YES' 95 | settings['CLANG_WARN_RANGE_LOOP_ANALYSIS'] = 'YES' 96 | settings['CLANG_WARN_STRICT_PROTOTYPES'] = 'YES' 97 | 98 | settings 99 | end 100 | 101 | def test_build_settings(settings) 102 | settings['LD_RUNPATH_SEARCH_PATHS'] = '$(inherited) @executable_path/Frameworks @loader_path/Frameworks' 103 | settings['LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]'] = '$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks' 104 | 105 | settings 106 | end 107 | 108 | def target_with_name(project, name) 109 | project.native_targets.detect { |e| e.name == name } 110 | end 111 | 112 | def generate 113 | project = Xcodeproj::Project.new(project_path) 114 | 115 | schemes_dir = Xcodeproj::XCScheme.user_data_dir(project.path) 116 | FileUtils.rm_rf(schemes_dir) 117 | FileUtils.mkdir_p(schemes_dir) 118 | 119 | xcschememanagement = {} 120 | xcschememanagement['SchemeUserState'] = {} 121 | xcschememanagement['SuppressBuildableAutocreation'] = {} 122 | 123 | project.build_configurations.each do |configuration| 124 | general_build_settings(configuration.build_settings) 125 | end 126 | 127 | @targets.each do |target| 128 | target.create(project, target.language, @scripts_directory) 129 | end 130 | 131 | @targets.each do |target| 132 | scheme = Xcodeproj::XCScheme.new 133 | 134 | created_target = target_with_name(project, target.name) 135 | 136 | scheme.add_build_target(created_target) 137 | 138 | unless scheme.launch_action.nil? 139 | target.add_launch_arguments(scheme.launch_action) 140 | target.add_environment_variables(scheme.launch_action) 141 | end 142 | 143 | if created_target.test_target_type? 144 | scheme.add_test_target(created_target) 145 | end 146 | 147 | unless target.test_target.nil? 148 | created_test_target = target_with_name(project, target.test_target.name) 149 | 150 | created_test_target.add_dependency(created_target) 151 | 152 | scheme.add_test_target(created_test_target) 153 | end 154 | 155 | unless scheme.test_action.nil? 156 | scheme.test_action.code_coverage_enabled = target.enable_code_coverage 157 | end 158 | 159 | scheme.save_as(project.path, target.name, true) 160 | xcschememanagement['SchemeUserState']["#{target.name}.xcscheme"] = {} 161 | xcschememanagement['SchemeUserState']["#{target.name}.xcscheme"]['isShown'] = true 162 | end 163 | 164 | project.native_targets.each do |target| 165 | next unless target.test_target_type? 166 | target.build_configurations.each do |configuration| 167 | test_build_settings(configuration.build_settings) 168 | end 169 | end 170 | 171 | xcschememanagement_path = schemes_dir + 'xcschememanagement.plist' 172 | Xcodeproj::Plist.write_to_path(xcschememanagement, xcschememanagement_path) 173 | 174 | project.save 175 | 176 | puts "Successfully generated #{project_path}" 177 | end 178 | end 179 | end 180 | -------------------------------------------------------------------------------- /lib/framework-generate/script.rb: -------------------------------------------------------------------------------- 1 | module FrameworkGenerate 2 | class Script 3 | attr_accessor :name, :script, :inputs 4 | 5 | def initialize(name = nil, script = nil, inputs = nil) 6 | @name = name 7 | @script = script 8 | @inputs = inputs 9 | 10 | yield(self) if block_given? 11 | end 12 | 13 | def to_s 14 | "Script<#{name}, #{script}, #{inputs}>" 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/framework-generate/target.rb: -------------------------------------------------------------------------------- 1 | require 'xcodeproj' 2 | require 'fileutils' 3 | 4 | module FrameworkGenerate 5 | class Target 6 | attr_accessor :name, :platforms, :language, :info_plist, :bundle_id, :header, :include_files, :exclude_files, :resource_files, :dependencies, :type, :pre_build_scripts, :post_build_scripts, :test_target, :is_safe_for_extensions, :enable_code_coverage, :launch_arguments, :environment_variables 7 | 8 | def initialize(name = nil, platforms = nil, language = nil, info_plist = nil, bundle_id = nil, header = nil, include_files = nil, exclude_files = nil, resource_files = nil, dependencies = nil, type = :framework, pre_build_scripts = nil, post_build_scripts = nil, test_target = nil, is_safe_for_extensions = false, enable_code_coverage = false, launch_arguments = nil, environment_variables = nil) 9 | @name = name 10 | @platforms = platforms 11 | @language = language 12 | @info_plist = info_plist 13 | @bundle_id = bundle_id 14 | @header = header 15 | @include_files = include_files 16 | @exclude_files = exclude_files 17 | @resource_files = resource_files 18 | @dependencies = dependencies 19 | @type = type 20 | @pre_build_scripts = pre_build_scripts 21 | @post_build_scripts = post_build_scripts 22 | @test_target = test_target 23 | @is_safe_for_extensions = is_safe_for_extensions 24 | @enable_code_coverage = enable_code_coverage 25 | @launch_arguments = launch_arguments 26 | @environment_variables = environment_variables 27 | 28 | yield(self) if block_given? 29 | end 30 | 31 | def to_s 32 | "Target<#{name}, #{info_plist}, #{bundle_id}, #{header}, #{include_files}, #{exclude_files}, #{dependencies}, #{type}, #{test_target}, #{is_safe_for_extensions}, #{enable_code_coverage}>" 33 | end 34 | 35 | def target_build_settings(settings, has_frameworks) 36 | settings.delete('CODE_SIGN_IDENTITY') 37 | settings.delete('CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING') 38 | settings.delete('CLANG_WARN_COMMA') 39 | settings.delete('CLANG_WARN_NON_LITERAL_NULL_CONVERSION') 40 | settings.delete('CLANG_WARN_OBJC_LITERAL_CONVERSION') 41 | settings.delete('CLANG_WARN_RANGE_LOOP_ANALYSIS') 42 | settings.delete('CLANG_WARN_STRICT_PROTOTYPES') 43 | 44 | settings['INFOPLIST_FILE'] = @info_plist 45 | settings['PRODUCT_BUNDLE_IDENTIFIER'] = @bundle_id 46 | settings['APPLICATION_EXTENSION_API_ONLY'] = @is_safe_for_extensions ? 'YES' : 'NO' 47 | settings['SUPPORTED_PLATFORMS'] = FrameworkGenerate::Platform.supported_platforms(@platforms) 48 | 49 | macos = FrameworkGenerate::Platform.find_platform(@platforms, :macos) 50 | unless macos.nil? 51 | settings['MACOSX_DEPLOYMENT_TARGET'] = FrameworkGenerate::Platform.deployment_target(macos) 52 | if has_frameworks 53 | settings['FRAMEWORK_SEARCH_PATHS[sdk=macosx*]'] = FrameworkGenerate::Platform.search_paths(macos) 54 | end 55 | end 56 | 57 | ios = FrameworkGenerate::Platform.find_platform(@platforms, :ios) 58 | unless ios.nil? 59 | settings['IPHONEOS_DEPLOYMENT_TARGET'] = FrameworkGenerate::Platform.deployment_target(ios) 60 | if has_frameworks 61 | settings['FRAMEWORK_SEARCH_PATHS[sdk=iphone*]'] = FrameworkGenerate::Platform.search_paths(ios) 62 | end 63 | end 64 | 65 | watchos = FrameworkGenerate::Platform.find_platform(@platforms, :watchos) 66 | unless watchos.nil? 67 | settings['WATCHOS_DEPLOYMENT_TARGET'] = FrameworkGenerate::Platform.deployment_target(watchos) 68 | if has_frameworks 69 | settings['FRAMEWORK_SEARCH_PATHS[sdk=watch*]'] = FrameworkGenerate::Platform.search_paths(watchos) 70 | end 71 | end 72 | 73 | tvos = FrameworkGenerate::Platform.find_platform(@platforms, :tvos) 74 | unless tvos.nil? 75 | settings['TVOS_DEPLOYMENT_TARGET'] = FrameworkGenerate::Platform.deployment_target(tvos) 76 | if has_frameworks 77 | settings['FRAMEWORK_SEARCH_PATHS[sdk=appletv*]'] = FrameworkGenerate::Platform.search_paths(tvos) 78 | end 79 | end 80 | 81 | settings['SWIFT_VERSION'] = @language.version 82 | 83 | settings 84 | end 85 | 86 | def find_group(project, path) 87 | folder_path = File.dirname(path) 88 | project.main_group.find_subpath(folder_path, true) 89 | end 90 | 91 | def add_framework_header(project, target) 92 | return if @header.nil? 93 | header_path = @header 94 | header_file_group = find_group(project, header_path) 95 | header_file = header_file_group.new_reference(header_path) 96 | header_build_file = target.headers_build_phase.add_file_reference(header_file, true) 97 | header_build_file.settings ||= {} 98 | header_build_file.settings['ATTRIBUTES'] = ['Public'] 99 | end 100 | 101 | def add_info_plist(project) 102 | info_plist_path = @info_plist 103 | info_plist_group = find_group(project, info_plist_path) 104 | has_info_plist = info_plist_group.find_file_by_path(info_plist_path) 105 | 106 | info_plist_group.new_reference(@info_plist) unless has_info_plist 107 | end 108 | 109 | def add_supporting_files(project, target) 110 | add_info_plist(project) 111 | return if target.test_target_type? 112 | add_framework_header(project, target) 113 | end 114 | 115 | def reject_excluded_files(exclude_files, path) 116 | exclude_files.each do |files_to_exclude| 117 | files_to_exclude.each do |file_to_exclude| 118 | return true if File.fnmatch(file_to_exclude, path) 119 | end 120 | end 121 | 122 | false 123 | end 124 | 125 | def add_source_files(project, target) 126 | exclude_files = if @exclude_files.nil? 127 | [] 128 | else 129 | @exclude_files.map do |files| 130 | Dir[files] 131 | end 132 | end 133 | 134 | source_files = @include_files.map do |files| 135 | Dir[files].reject do |path| 136 | reject_excluded_files(exclude_files, path) 137 | end 138 | end 139 | 140 | source_files.each do |file_directory| 141 | file_directory.each do |path| 142 | source_file_group = find_group(project, path) 143 | has_source_file = source_file_group.find_file_by_path(path) 144 | unless has_source_file 145 | source_file = source_file_group.new_reference(path) 146 | target.source_build_phase.add_file_reference(source_file, true) 147 | end 148 | end 149 | end 150 | end 151 | 152 | def append_framework_extension(framework) 153 | return framework if File.extname(framework) == '.framework' 154 | 155 | "#{framework}.framework" 156 | end 157 | 158 | def add_dependencies(project, target) 159 | return if @dependencies.nil? 160 | 161 | dependency_names = @dependencies.map do |dependency| 162 | append_framework_extension(dependency) 163 | end 164 | 165 | frameworks = dependency_names.reject do |name| 166 | !project.products.any? { |x| x.path == name } 167 | end 168 | 169 | frameworks = frameworks.map do |name| 170 | project.products.find { |x| x.path == name } 171 | end 172 | 173 | frameworks.each do |path| 174 | target.frameworks_build_phase.add_file_reference(path, true) 175 | end 176 | end 177 | 178 | def copy_carthage_frameworks(project, build_phase, scripts_directory) 179 | script_file_name = 'copy-carthage-frameworks.sh' 180 | script_file_path = File.join(File.dirname(__FILE__), script_file_name) 181 | 182 | if scripts_directory.nil? 183 | script_file = File.open(script_file_path) 184 | 185 | build_phase.shell_script = script_file.read 186 | 187 | script_file.close 188 | else 189 | script_path = File.join(Dir.pwd, scripts_directory, script_file_name) 190 | dirname = File.dirname(script_path) 191 | FileUtils.mkdir_p(dirname) unless File.directory?(dirname) 192 | 193 | FileUtils.cp(script_file_path, script_path, preserve: true) 194 | 195 | xcode_path = File.join('${SRCROOT}', scripts_directory, script_file_name) 196 | build_phase.shell_script = " exec \"#{xcode_path}\"" 197 | end 198 | 199 | add_framework_to_copy_phase(project, build_phase) 200 | end 201 | 202 | def third_party_frameworks?(project) 203 | return if @dependencies.nil? 204 | 205 | dependency_names = @dependencies.map do |dependency| 206 | append_framework_extension(dependency) 207 | end 208 | 209 | frameworks = dependency_names.reject do |name| 210 | project.products.any? { |x| x.path == name } 211 | end 212 | 213 | frameworks.length > 0 214 | end 215 | 216 | def add_framework_to_copy_phase(project, build_phase) 217 | return if @dependencies.nil? 218 | 219 | dependency_names = @dependencies.map do |dependency| 220 | append_framework_extension(dependency) 221 | end 222 | 223 | frameworks = dependency_names.reject do |name| 224 | project.products.any? { |x| x.path == name } 225 | end 226 | 227 | frameworks.each do |path| 228 | build_phase.input_paths << path 229 | end 230 | end 231 | 232 | def add_resource_files(project, target) 233 | return if @resource_files.nil? 234 | 235 | files = @resource_files.map do |files| 236 | Dir[files] 237 | end 238 | 239 | files.each do |file_directory| 240 | file_directory.each do |path| 241 | file_group = find_group(project, path) 242 | has_file = file_group.find_file_by_path(path) 243 | unless has_file 244 | file = file_group.new_reference(path) 245 | target.resources_build_phase.add_file_reference(file, true) 246 | end 247 | end 248 | end 249 | end 250 | 251 | def add_build_scripts(target, scripts) 252 | return if scripts.nil? 253 | 254 | scripts.each do |script| 255 | build_phase = target.new_shell_script_build_phase(script.name) 256 | build_phase.shell_script = script.script 257 | build_phase.input_paths = script.inputs 258 | end 259 | end 260 | 261 | def add_pre_build_scripts(target) 262 | add_build_scripts(target, @pre_build_scripts) 263 | end 264 | 265 | def add_post_build_scripts(target) 266 | add_build_scripts(target, @post_build_scripts) 267 | end 268 | 269 | def add_launch_arguments(launch_action) 270 | return if @launch_arguments.nil? 271 | 272 | command_line_arguments = @launch_arguments.map do |launch_arguement| 273 | { argument: launch_arguement, enabled: true } 274 | end 275 | 276 | launch_action.command_line_arguments = Xcodeproj::XCScheme::CommandLineArguments.new(command_line_arguments) 277 | end 278 | 279 | def add_environment_variables(launch_action) 280 | return if @environment_variables.nil? 281 | 282 | environment_variables = @environment_variables.map do |key, value| 283 | { key: key, value: value, enabled: true } 284 | end 285 | 286 | launch_action.environment_variables = Xcodeproj::XCScheme::EnvironmentVariables.new(environment_variables) 287 | end 288 | 289 | def create(project, language, scripts_directory) 290 | name = @name 291 | type = @type 292 | 293 | # Target 294 | target = project.new(Xcodeproj::Project::Object::PBXNativeTarget) 295 | project.targets << target 296 | target.name = name 297 | target.product_name = name 298 | target.product_type = Xcodeproj::Constants::PRODUCT_TYPE_UTI[type] 299 | target.build_configuration_list = Xcodeproj::Project::ProjectHelper.configuration_list(project, :osx, nil, type, language.type) 300 | 301 | has_frameworks = third_party_frameworks?(project) 302 | 303 | # Pre build script 304 | add_pre_build_scripts(target) 305 | 306 | add_supporting_files(project, target) 307 | add_source_files(project, target) 308 | 309 | target.build_configurations.each do |configuration| 310 | target_build_settings(configuration.build_settings, has_frameworks) 311 | end 312 | 313 | # Product 314 | product = project.products_group.new_product_ref_for_target(name, type) 315 | target.product_reference = product 316 | 317 | # Build phases 318 | 319 | target.build_phases << project.new(Xcodeproj::Project::Object::PBXResourcesBuildPhase) 320 | target.build_phases << project.new(Xcodeproj::Project::Object::PBXFrameworksBuildPhase) 321 | 322 | # Post build script 323 | add_post_build_scripts(target) 324 | 325 | # Dependencies 326 | add_dependencies(project, target) 327 | 328 | # Resource files 329 | add_resource_files(project, target) 330 | 331 | # Copy frameworks to test target 332 | if target.test_target_type? && has_frameworks 333 | build_phase = target.new_shell_script_build_phase('Copy Carthage Frameworks') 334 | copy_carthage_frameworks(project, build_phase, scripts_directory) 335 | end 336 | 337 | target 338 | end 339 | end 340 | end 341 | --------------------------------------------------------------------------------