longbow 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +43 -0
- data/LICENSE.txt +22 -0
- data/README.md +122 -0
- data/Rakefile +9 -0
- data/bin/longbow +24 -0
- data/lib/longbow/colors.rb +19 -0
- data/lib/longbow/commands/install.rb +47 -0
- data/lib/longbow/commands/shoot.rb +69 -0
- data/lib/longbow/commands.rb +4 -0
- data/lib/longbow/images.rb +165 -0
- data/lib/longbow/plist.rb +68 -0
- data/lib/longbow/targets.rb +110 -0
- data/lib/longbow/version.rb +3 -0
- data/lib/longbow.rb +6 -0
- data/longbow-0.0.1.gem +0 -0
- data/longbow.gemspec +35 -0
- data/resources/banner.png +0 -0
- metadata +178 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 96a0a8923328aceeec013bb664110ef3d068523a
|
4
|
+
data.tar.gz: d84d3828e7aaf15061d8f5ed54e0eb591f84898e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c05a5c08829a5e7b02fd7995d93c52352681a596a8b5bc7619275b94556231b69151d7ae4b08b3b560088625f0c5fa2dd8064926b1d156290f96d932ca2d28dc
|
7
|
+
data.tar.gz: 74047f1e332771e912c69fe00a0817de6595d626fec6e2bd079b9d764fb9f7626f7caf22b87d1e387a0099b9341f2ae0d98cc463a948df46fa6cc8723e63f2a5
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
longbow (0.0.1)
|
5
|
+
bundler (~> 1.3)
|
6
|
+
commander (~> 4.1)
|
7
|
+
dotenv (~> 0.7)
|
8
|
+
fileutils
|
9
|
+
json
|
10
|
+
mini_magick (~> 3.7.0)
|
11
|
+
xcodeproj
|
12
|
+
|
13
|
+
GEM
|
14
|
+
remote: https://rubygems.org/
|
15
|
+
specs:
|
16
|
+
activesupport (3.2.16)
|
17
|
+
i18n (~> 0.6, >= 0.6.4)
|
18
|
+
multi_json (~> 1.0)
|
19
|
+
colored (1.2)
|
20
|
+
commander (4.1.5)
|
21
|
+
highline (~> 1.6.11)
|
22
|
+
dotenv (0.10.0)
|
23
|
+
fileutils (0.7)
|
24
|
+
rmagick (>= 2.13.1)
|
25
|
+
highline (1.6.20)
|
26
|
+
i18n (0.6.9)
|
27
|
+
json (1.8.1)
|
28
|
+
mini_magick (3.7.0)
|
29
|
+
subexec (~> 0.2.1)
|
30
|
+
multi_json (1.9.2)
|
31
|
+
rake (10.1.1)
|
32
|
+
rmagick (2.13.2)
|
33
|
+
subexec (0.2.3)
|
34
|
+
xcodeproj (0.16.0)
|
35
|
+
activesupport (~> 3.0)
|
36
|
+
colored (~> 1.2)
|
37
|
+
|
38
|
+
PLATFORMS
|
39
|
+
ruby
|
40
|
+
|
41
|
+
DEPENDENCIES
|
42
|
+
longbow!
|
43
|
+
rake
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Intermark Group
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
![banner](resources/banner.png)
|
2
|
+
|
3
|
+
**Problem**
|
4
|
+
|
5
|
+
One codebase. Multiple App Store submissions with different icons, info.plist keys, etc.
|
6
|
+
|
7
|
+
**Solution**
|
8
|
+
|
9
|
+
```
|
10
|
+
longbow install
|
11
|
+
longbow shoot
|
12
|
+
```
|
13
|
+
|
14
|
+
**About**
|
15
|
+
|
16
|
+
Longbow duplicates the main target in your `.xcworkspace` or `.xcodeproj` file, then reads from a JSON file to fill out the rest of your target. It looks for certain keys and creates such things like taking an icon image and resizing it for the various icons you'll need, and adding keys to the info.plist file for that target. The goal was to be practically autonomous in creating new targets and apps.
|
17
|
+
|
18
|
+
**Requirements**
|
19
|
+
|
20
|
+
Longbow requires Xcode 5+, and your app must use the new .xcassets paradigm for icons, launch screens, etc.
|
21
|
+
|
22
|
+
## Table of Contents
|
23
|
+
|
24
|
+
* [Installation](#installation)
|
25
|
+
* [Set Up](#set-up)
|
26
|
+
* [Formatting longbow.json](#formatting-longbow-json)
|
27
|
+
* [Create a Target](#create-a-target)
|
28
|
+
* [Global Options](#global-options)
|
29
|
+
* [Contributing](#contributing)
|
30
|
+
|
31
|
+
## Installation
|
32
|
+
|
33
|
+
Add this line to your application's Gemfile:
|
34
|
+
|
35
|
+
gem 'longbow'
|
36
|
+
|
37
|
+
And then execute:
|
38
|
+
|
39
|
+
$ bundle
|
40
|
+
|
41
|
+
Or install it yourself as:
|
42
|
+
|
43
|
+
$ gem install longbow
|
44
|
+
|
45
|
+
## Set Up
|
46
|
+
|
47
|
+
Run `longbow install` in the directory where your `.xcworkspace` or `.xcodeproj` file lives. This will create a file, `longbow.json`, where they will be used to build out from here. You are almost ready to start creating new targets
|
48
|
+
|
49
|
+
## Formatting .longbow.json
|
50
|
+
|
51
|
+
Here's a basic gist of how to format your `longbow.json` file:
|
52
|
+
|
53
|
+
```
|
54
|
+
{
|
55
|
+
"targets":[
|
56
|
+
{
|
57
|
+
"name":"TargetName",
|
58
|
+
"icon_url":"https://somewhere.net/img.png",
|
59
|
+
"info_plist": {
|
60
|
+
"CFBundleIdentifier":"com.company.target1",
|
61
|
+
"ProprietaryKey":"Value"
|
62
|
+
}
|
63
|
+
},
|
64
|
+
{
|
65
|
+
"name":"TargetName2",
|
66
|
+
"icon_path":"/relative/path/to/file.png",
|
67
|
+
"info_plist": {
|
68
|
+
"CFBundleIdentifier":"com.company.target2",
|
69
|
+
"ProprietaryKey":"Value2"
|
70
|
+
}
|
71
|
+
}
|
72
|
+
],
|
73
|
+
"global_info_keys":{
|
74
|
+
"somekey":"somevalue"
|
75
|
+
},
|
76
|
+
"devices":["iPhone","iPad"]
|
77
|
+
}
|
78
|
+
```
|
79
|
+
|
80
|
+
In the top-level of the JSON file, we have 3 key/value pairs:
|
81
|
+
|
82
|
+
* `targets`
|
83
|
+
* `devices`
|
84
|
+
* `global_info_keys`
|
85
|
+
|
86
|
+
The `targets` section contains nested key/value pairs for each specific target. Each target can contain the following keys:
|
87
|
+
|
88
|
+
* `icon_url` or `icon_path`
|
89
|
+
* `info_plist`
|
90
|
+
* `name`
|
91
|
+
|
92
|
+
The `icon_url` and `icon_path` key corresponds to the location of the icon image. It will be downloaded from the web if necessary, then resized depending on your device setting and added to the Images.xcassets file for that target. The `info_plist` key corresponds to another set of key/value pairs that will be added or updated in the info.plist file specifically for this target.
|
93
|
+
|
94
|
+
## Creating/Updating a Target
|
95
|
+
|
96
|
+
Now that you're set up - it's time to add a target. Make sure that you have updated your `longbow.json` file with the correct information for your target, and then run the following command inside the project directory.
|
97
|
+
|
98
|
+
`longbow shoot -n NameOfTarget`
|
99
|
+
|
100
|
+
What this does is goes to your `.longbow.json` file and looks for json{"targets"}{"NameOfTarget"} and tries to create a new Target in your app, and handles the various icons/info_plist additions specifically for this target. If your target already exists, it will just update the icon images and plist settings.
|
101
|
+
|
102
|
+
If you leave off the `-n` option, it will run for all targets in the `.longbow.json` file.
|
103
|
+
|
104
|
+
**Other Options**
|
105
|
+
|
106
|
+
* `-d, --directory` - if not in the current directory, specify a new path
|
107
|
+
|
108
|
+
`longbow shoot -n NameOfTarget -d ~/Path/To/App`
|
109
|
+
|
110
|
+
## Global Options
|
111
|
+
|
112
|
+
`--dontlog` will not log the status/operations to the console.
|
113
|
+
|
114
|
+
`--help` will fill you in on what you need to do for an action.
|
115
|
+
|
116
|
+
## Contributing
|
117
|
+
|
118
|
+
1. Fork it
|
119
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
120
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
121
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
122
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/longbow
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'dotenv'
|
4
|
+
Dotenv.load
|
5
|
+
|
6
|
+
require 'commander/import'
|
7
|
+
|
8
|
+
$:.push File.expand_path("../../lib", __FILE__)
|
9
|
+
require 'longbow'
|
10
|
+
|
11
|
+
HighLine.track_eof = false # Fix for built-in Ruby
|
12
|
+
|
13
|
+
program :version, Longbow::VERSION
|
14
|
+
program :description, 'Create duplicate Targets for your Xcode workspace/project.'
|
15
|
+
|
16
|
+
program :help, 'Author', 'Benjamin Gordon (@bennyguitar) for Intermark Group <interactive@intermarkgroup.com>'
|
17
|
+
program :help, 'Website', 'https://github.com/intermark'
|
18
|
+
program :help_formatter, :compact
|
19
|
+
|
20
|
+
global_option('--dontlog') { $nolog = true }
|
21
|
+
|
22
|
+
default_command :help
|
23
|
+
|
24
|
+
require 'longbow/commands'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Longbow
|
2
|
+
# Main Colorize Functions
|
3
|
+
def self.colorize(text, color_code)
|
4
|
+
puts "\e[#{color_code}m#{text}\e[0m"
|
5
|
+
end
|
6
|
+
|
7
|
+
# Specific Colors
|
8
|
+
def self.red(text)
|
9
|
+
colorize(text, 31)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.green(text)
|
13
|
+
colorize(text, 32)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.blue(text)
|
17
|
+
colorize(text, 36)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
$:.push File.expand_path('../../', __FILE__)
|
2
|
+
require 'fileutils'
|
3
|
+
require 'longbow'
|
4
|
+
|
5
|
+
command :install do |c|
|
6
|
+
c.syntax = 'longbow install [options]'
|
7
|
+
c.summary = 'Creates the required files in your directory.'
|
8
|
+
c.description = ''
|
9
|
+
c.option '-d', '--directory DIRECTORY', 'Path where the .xcproj or .xcworkspace file && the longbow.json file live.'
|
10
|
+
|
11
|
+
c.action do |args, options|
|
12
|
+
@directory = options.directory ? options.directory : Dir.pwd
|
13
|
+
@json_path = @directory + '/longbow.json'
|
14
|
+
|
15
|
+
if File.exist?(@json_path)
|
16
|
+
Longbow::red ' longbow.json already exists at ' + @json_path
|
17
|
+
else
|
18
|
+
File.open(@json_path, 'w') do |f|
|
19
|
+
f.write('{
|
20
|
+
"targets":[
|
21
|
+
{
|
22
|
+
"name":"TargetName",
|
23
|
+
"icon_url":"https://somewhere.net/img.png",
|
24
|
+
"info_plist": {
|
25
|
+
"CFBundleIdentifier":"com.company.target1",
|
26
|
+
"ProprietaryKey":"Value"
|
27
|
+
}
|
28
|
+
},
|
29
|
+
{
|
30
|
+
"name":"TargetName2",
|
31
|
+
"icon_path":"/relative/path/to/file.png",
|
32
|
+
"info_plist": {
|
33
|
+
"CFBundleIdentifier":"com.company.target2",
|
34
|
+
"ProprietaryKey":"Value2"
|
35
|
+
}
|
36
|
+
}
|
37
|
+
],
|
38
|
+
"global_info_keys":{
|
39
|
+
"somekey":"somevalue"
|
40
|
+
},
|
41
|
+
"devices":["iPhone","iPad"]
|
42
|
+
}')
|
43
|
+
end
|
44
|
+
Longbow::green ' longbow.json created' unless $nolog
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
$:.push File.expand_path('../../', __FILE__)
|
2
|
+
require 'fileutils'
|
3
|
+
require 'longbow/colors'
|
4
|
+
require 'longbow/targets'
|
5
|
+
require 'longbow/images'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
command :shoot do |c|
|
9
|
+
c.syntax = 'longbow shoot [options]'
|
10
|
+
c.summary = 'Creates/updates a target or all targets in your workspace or project.'
|
11
|
+
c.description = ''
|
12
|
+
|
13
|
+
c.option '-n', '--name NAME', 'Target name from the corresponding longbow.json file.'
|
14
|
+
c.option '-d', '--directory DIRECTORY', 'Path where the .xcodeproj or .xcworkspace file && the longbow.json file live.'
|
15
|
+
|
16
|
+
c.action do |args, options|
|
17
|
+
# Set Up
|
18
|
+
@target_name = options.name ? options.name : nil
|
19
|
+
@directory = options.directory ? options.directory : Dir.pwd
|
20
|
+
@targets = []
|
21
|
+
|
22
|
+
# Check for .longbow.json
|
23
|
+
@json_path = @directory + '/longbow.json'
|
24
|
+
if !File.exists?(@json_path)
|
25
|
+
puts
|
26
|
+
Longbow::red " Couldn't find longbow.json at " + @json_path
|
27
|
+
puts
|
28
|
+
puts " Run this command to install the correct files:"
|
29
|
+
puts " longbow install"
|
30
|
+
puts
|
31
|
+
next
|
32
|
+
end
|
33
|
+
|
34
|
+
# Create an Object from JSON
|
35
|
+
json_contents = File.open(@json_path).read
|
36
|
+
unless !!JSON.parse(json_contents)
|
37
|
+
Longbow::red ' Invalid JSON - lint it, and try again.'
|
38
|
+
next
|
39
|
+
end
|
40
|
+
obj = JSON.parse(json_contents)
|
41
|
+
|
42
|
+
# Check for Target Name
|
43
|
+
if @target_name
|
44
|
+
obj['targets'].each do |t|
|
45
|
+
@targets << t if t['name'] == @target_name
|
46
|
+
end
|
47
|
+
|
48
|
+
if @targets.length == 0
|
49
|
+
puts
|
50
|
+
Longbow::red " Couldn't find a target named " + @target_name + " in the .longbow.json file."
|
51
|
+
puts
|
52
|
+
next
|
53
|
+
end
|
54
|
+
else
|
55
|
+
obj['targets'].each do |t|
|
56
|
+
@targets << t
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Begin
|
61
|
+
@targets.each do |t|
|
62
|
+
Longbow::update_target @directory, t['name'], obj['global_info_keys'], t['info_plist']
|
63
|
+
Longbow::resize_icons @directory, t, obj
|
64
|
+
Longbow::write_json_for_icons @directory, t, obj
|
65
|
+
Longbow::green ' Finished: ' + t['name'] unless $nolog
|
66
|
+
puts unless $nolog
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
$:.push File.expand_path('../', __FILE__)
|
2
|
+
require 'mini_magick'
|
3
|
+
require 'colors'
|
4
|
+
require 'xcodeproj'
|
5
|
+
require 'open-uri'
|
6
|
+
|
7
|
+
module Longbow
|
8
|
+
|
9
|
+
# Resize Icon
|
10
|
+
def self.resize_icons(directory, t, obj)
|
11
|
+
# Bad Params
|
12
|
+
if !directory || !t
|
13
|
+
return false
|
14
|
+
end
|
15
|
+
|
16
|
+
# Set Up
|
17
|
+
target = t['name']
|
18
|
+
|
19
|
+
# Get Device Information
|
20
|
+
iPhone = false
|
21
|
+
iPad = false
|
22
|
+
if obj['devices']
|
23
|
+
obj['devices'].each do |d|
|
24
|
+
iPhone = true if d == 'iPhone'
|
25
|
+
iPad = true if d == 'iPad'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Get Image Information
|
30
|
+
img_path = ''
|
31
|
+
if t['icon_url']
|
32
|
+
img_path = self.path_for_downloaded_image_from_url directory, target, t['icon_url']
|
33
|
+
elsif t['icon_path']
|
34
|
+
img_path = directory + '/' + t['icon_path']
|
35
|
+
end
|
36
|
+
|
37
|
+
# Make directory
|
38
|
+
img_dir = make_asset_directory directory, target
|
39
|
+
|
40
|
+
# Make image
|
41
|
+
image = MiniMagick::Image.open(img_path)
|
42
|
+
return false unless image
|
43
|
+
|
44
|
+
# Size for iPhone
|
45
|
+
if iPhone
|
46
|
+
image.resize '120x120'
|
47
|
+
image.write img_dir + '/icon120x120.png'
|
48
|
+
image.resize '114x114'
|
49
|
+
image.write img_dir + '/icon114x114.png'
|
50
|
+
image.resize '80x80'
|
51
|
+
image.write img_dir + '/icon80x80.png'
|
52
|
+
image.resize '58x58'
|
53
|
+
image.write img_dir + '/icon58x58.png'
|
54
|
+
image.resize '57x57'
|
55
|
+
image.write img_dir + '/icon57x57.png'
|
56
|
+
image.resize '29x29'
|
57
|
+
image.write img_dir + '/icon29x29.png'
|
58
|
+
Longbow::green (' - Created iPhone icon images for ' + target) if $verbose
|
59
|
+
end
|
60
|
+
|
61
|
+
if iPad
|
62
|
+
image.resize '152x152'
|
63
|
+
image.write img_dir + '/icon152x152.png'
|
64
|
+
image.resize '144x144'
|
65
|
+
image.write img_dir + '/icon144x144.png'
|
66
|
+
image.resize '100x100'
|
67
|
+
image.write img_dir + '/icon100x100.png'
|
68
|
+
image.resize '80x80'
|
69
|
+
image.write img_dir + '/icon80x80.png'
|
70
|
+
image.resize '76x76'
|
71
|
+
image.write img_dir + '/icon76x76.png'
|
72
|
+
image.resize '72x72'
|
73
|
+
image.write img_dir + '/icon72x72.png'
|
74
|
+
image.resize '58x58'
|
75
|
+
image.write img_dir + '/icon58x58.png'
|
76
|
+
image.resize '50x50'
|
77
|
+
image.write img_dir + '/icon50x50.png'
|
78
|
+
image.resize '40x40'
|
79
|
+
image.write img_dir + '/icon40x40.png'
|
80
|
+
image.resize '29x29'
|
81
|
+
image.write img_dir + '/icon29x29.png'
|
82
|
+
Longbow::green (' - Created iPad icon images for ' + target) unless $nolog
|
83
|
+
end
|
84
|
+
return true
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
# Create JSON
|
89
|
+
def self.write_json_for_icons(directory, t, obj)
|
90
|
+
# Bad Params
|
91
|
+
if !directory || !t
|
92
|
+
return false
|
93
|
+
end
|
94
|
+
|
95
|
+
# Set Up
|
96
|
+
target = t['name']
|
97
|
+
|
98
|
+
# Get Device Information
|
99
|
+
iPhone = false
|
100
|
+
iPad = false
|
101
|
+
if obj['devices']
|
102
|
+
obj['devices'].each do |d|
|
103
|
+
iPhone = true if d == 'iPhone'
|
104
|
+
iPad = true if d == 'iPad'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Make directory
|
109
|
+
img_dir = make_asset_directory directory, target
|
110
|
+
|
111
|
+
# Write the JSON file
|
112
|
+
File.open(img_dir + '/Contents.json', 'w') do |f|
|
113
|
+
f.write('{ "images" : [ ')
|
114
|
+
|
115
|
+
if iPhone
|
116
|
+
f.write( '{ "size" : "29x29", "idiom" : "iphone", "filename" : "icon58x58.png", "scale" : "2x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "icon80x80.png", "scale" : "2x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "icon120x120.png", "scale" : "2x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "icon29x29.png", "scale" : "1x" }, { "size" : "57x57", "idiom" : "iphone", "filename" : "icon57x57.png", "scale" : "1x" }, { "size" : "57x57", "idiom" : "iphone", "filename" : "icon114x114.png", "scale" : "2x" }' + (iPad ? ',' : ''))
|
117
|
+
end
|
118
|
+
|
119
|
+
if iPad
|
120
|
+
f.write( '{ "idiom" : "ipad", "size" : "29x29", "scale" : "1x", "filename" : "icon29x29.png" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x", "filename" : "icon58x58.png" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x", "filename" : "icon40x40.png" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x", "filename" : "icon80x80.png" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x", "filename" : "icon76x76.png" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x", "filename" : "icon152x152.png" }, { "idiom" : "ipad", "size" : "50x50", "scale" : "1x", "filename" : "icon50x50.png" }, { "idiom" : "ipad", "size" : "50x50", "scale" : "2x", "filename" : "icon100x100.png" }, { "idiom" : "ipad", "size" : "72x72", "scale" : "1x", "filename" : "icon72x72.png" }, { "idiom" : "ipad", "size" : "72x72", "scale" : "2x", "filename" : "icon144x144.png" }')
|
121
|
+
end
|
122
|
+
|
123
|
+
f.write(' ], "info" : { "version" : 1, "author" : "xcode" }, "properties" : { "pre-rendered" : true } }')
|
124
|
+
end
|
125
|
+
|
126
|
+
# Return true
|
127
|
+
Longbow::green (' - Created Images.xcassets icon set for ' + target) unless $nolog
|
128
|
+
return true
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
# Make Directory for Images.xcassets
|
133
|
+
def self.make_asset_directory directory, target
|
134
|
+
asset_path = assets_file_path directory
|
135
|
+
full_path = asset_path + '/' + target + '.appiconset/'
|
136
|
+
FileUtils::mkdir_p full_path
|
137
|
+
return full_path
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
def self.assets_file_path directory
|
142
|
+
asset_path = ''
|
143
|
+
Dir.glob(directory + '/**/*/').each do |d|
|
144
|
+
searching = 'Images.xcassets/'
|
145
|
+
asset_path = d if d.slice(d.length - searching.length, searching.length) == searching
|
146
|
+
end
|
147
|
+
|
148
|
+
return asset_path
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
# Download Image from URL
|
153
|
+
def self.path_for_downloaded_image_from_url directory, target, url
|
154
|
+
img_path = directory + '/resources/icons/'
|
155
|
+
img_file_name = target + '.png'
|
156
|
+
FileUtils::mkdir_p img_path
|
157
|
+
File.open(img_path + img_file_name, 'wb') do |f|
|
158
|
+
f.write open(url).read
|
159
|
+
end
|
160
|
+
|
161
|
+
return img_path + img_file_name
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
$:.push File.expand_path('../', __FILE__)
|
2
|
+
require 'mini_magick'
|
3
|
+
require 'colors'
|
4
|
+
require 'xcodeproj'
|
5
|
+
require 'open-uri'
|
6
|
+
|
7
|
+
module Longbow
|
8
|
+
|
9
|
+
# Create Plist from Original Plist Content
|
10
|
+
def self.create_plist_from_old_plist old_plist, info_hash, global_hash
|
11
|
+
return '' unless old_plist && info_hash && global_hash
|
12
|
+
plist_text = old_plist
|
13
|
+
[info_hash,global_hash].each do |hash|
|
14
|
+
hash.each_key do |k|
|
15
|
+
value = hash[k]
|
16
|
+
matches = plist_text.match /<key>#{k}<\/key>\s*<(.*?)>.*<\/(.*?)>/
|
17
|
+
if matches
|
18
|
+
plist_text = plist_text.sub(matches[0], "<key>" + k + "</key>\n" + recursive_plist_value_for_value(value))
|
19
|
+
else
|
20
|
+
plist_text = plist_text.sub(/<\/dict>\s*<\/plist>/, "<key>" + k + "</key>\n" + recursive_plist_value_for_value(value) + "</dict></plist>")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
return plist_text
|
26
|
+
end
|
27
|
+
|
28
|
+
# Recursively create Plist Values for a given object
|
29
|
+
def self.recursive_plist_value_for_value value
|
30
|
+
return '' unless value
|
31
|
+
|
32
|
+
# Check Number
|
33
|
+
if value.kind_of?(Numeric)
|
34
|
+
return '<real>' + value.to_s + '</real>'
|
35
|
+
end
|
36
|
+
|
37
|
+
# Check Boolean
|
38
|
+
if !!value == value
|
39
|
+
if value == true
|
40
|
+
return '<true />'
|
41
|
+
else
|
42
|
+
return '<false />'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Check Array
|
47
|
+
if value.kind_of?(Array)
|
48
|
+
total_values = '<array>'
|
49
|
+
value.each do |v|
|
50
|
+
total_values += recursive_plist_value_for_value(v)
|
51
|
+
end
|
52
|
+
return total_values + '</array>'
|
53
|
+
end
|
54
|
+
|
55
|
+
# Check Hash
|
56
|
+
if value.kind_of?(Hash)
|
57
|
+
total_values = '<dict>'
|
58
|
+
value.each_key do |key|
|
59
|
+
total_values += '<key>' + key + '</key>'
|
60
|
+
total_values += recursive_plist_value_for_value value[key]
|
61
|
+
end
|
62
|
+
return total_values + '</dict>'
|
63
|
+
end
|
64
|
+
|
65
|
+
return '<string>' + value.to_s + '</string>'
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'xcodeproj'
|
2
|
+
require 'colors'
|
3
|
+
require 'plist'
|
4
|
+
|
5
|
+
module Longbow
|
6
|
+
|
7
|
+
def self.update_target directory, target, global_keys, info_keys
|
8
|
+
unless directory && target && global_keys && info_keys
|
9
|
+
Longbow::red ' Invalid parameters. Could not create/update target named: ' + target
|
10
|
+
return false
|
11
|
+
end
|
12
|
+
|
13
|
+
# Find Project File
|
14
|
+
project_paths = []
|
15
|
+
Dir.foreach(directory) do |fname|
|
16
|
+
project_paths << fname if fname.include? '.xcodeproj'
|
17
|
+
end
|
18
|
+
|
19
|
+
# Open The Project
|
20
|
+
return false if project_paths.length == 0
|
21
|
+
proj = Xcodeproj::Project.open(project_paths[0])
|
22
|
+
|
23
|
+
# Get Main Target's Basic Info
|
24
|
+
@target = nil
|
25
|
+
proj.targets.each do |t|
|
26
|
+
if t.to_s == target
|
27
|
+
@target = t
|
28
|
+
Longbow::green ' ' + target + ' found.' unless $nolog
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Create Target if Necessary
|
33
|
+
main_target = proj.targets.first
|
34
|
+
@target = create_target(proj, target) unless @target
|
35
|
+
|
36
|
+
# Plist Creating/Adding
|
37
|
+
main_plist = main_target.build_configurations[0].build_settings['INFOPLIST_FILE']
|
38
|
+
main_plist_contents = File.read(directory + '/' + main_plist)
|
39
|
+
target_plist_path = directory + '/' + main_plist.split('/')[0] + '/' + target + '-info.plist'
|
40
|
+
plist_text = Longbow::create_plist_from_old_plist main_plist_contents, info_keys, global_keys
|
41
|
+
File.open(target_plist_path, 'w') do |f|
|
42
|
+
f.write(plist_text)
|
43
|
+
end
|
44
|
+
Longbow::green ' - ' + target + '-info.plist Updated.' unless $nolog
|
45
|
+
|
46
|
+
|
47
|
+
# Add Build Settings
|
48
|
+
@target.build_configurations.each do |b|
|
49
|
+
# Main Settings
|
50
|
+
main_settings = nil
|
51
|
+
main_target.build_configurations.each do |bc|
|
52
|
+
main_settings = bc.build_settings if bc.to_s == b.to_s
|
53
|
+
end
|
54
|
+
settings = b.build_settings
|
55
|
+
main_settings.each_key do |key|
|
56
|
+
settings[key] = main_settings[key]
|
57
|
+
end
|
58
|
+
|
59
|
+
# Plist & Icons
|
60
|
+
settings['INFOPLIST_FILE'] = main_plist.split('/')[0] + '/' + target + '-info.plist'
|
61
|
+
settings['ASSETCATALOG_COMPILER_APPICON_NAME'] = target
|
62
|
+
|
63
|
+
if File.exists? directory + '/Pods'
|
64
|
+
settings['PODS_ROOT'] = '${SRCROOT}/Pods'
|
65
|
+
settings['HEADER_SEARCH_PATHS'] = ['${PODS_ROOT}/Headers/**']
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Save The Project
|
70
|
+
proj.save
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.create_target project, target
|
74
|
+
main_target = project.targets.first
|
75
|
+
deployment_target = main_target.deployment_target
|
76
|
+
|
77
|
+
# Create New Target
|
78
|
+
new_target = Xcodeproj::Project::ProjectHelper.new_target project, :application, target, :ios, deployment_target, project.products_group
|
79
|
+
if new_target
|
80
|
+
# Add Build Phases
|
81
|
+
main_target.build_phases.objects.each do |b|
|
82
|
+
if b.isa == 'PBXSourcesBuildPhase'
|
83
|
+
b.files_references.each do |f|
|
84
|
+
new_target.source_build_phase.add_file_reference f
|
85
|
+
end
|
86
|
+
elsif b.isa == 'PBXFrameworksBuildPhase'
|
87
|
+
b.files_references.each do |f|
|
88
|
+
new_target.frameworks_build_phase.add_file_reference f
|
89
|
+
end
|
90
|
+
elsif b.isa == 'PBXResourcesBuildPhase'
|
91
|
+
b.files_references.each do |f|
|
92
|
+
new_target.resources_build_phase.add_file_reference f
|
93
|
+
end
|
94
|
+
elsif b.isa == 'PBXShellScriptBuildPhase'
|
95
|
+
phase = new_target.new_shell_script_build_phase(name = b.display_name)
|
96
|
+
phase.shell_script = b.shell_script
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
Longbow::green ' ' + target + ' created.' unless $nolog
|
101
|
+
else
|
102
|
+
puts
|
103
|
+
Longbow::red ' Target Creation failed for target named: ' + target
|
104
|
+
puts
|
105
|
+
end
|
106
|
+
|
107
|
+
return new_target
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
data/lib/longbow.rb
ADDED
data/longbow-0.0.1.gem
ADDED
Binary file
|
data/longbow.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'longbow/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "longbow"
|
8
|
+
spec.version = Longbow::VERSION
|
9
|
+
spec.authors = ["Intermark Interactive"]
|
10
|
+
spec.email = ["interactive@intermarkgroup.com"]
|
11
|
+
spec.description = "Duplicates the main target of an xcodeproj/xcworkspace and renames it, its info.plist, and its image.xcassets as well. It then optionally reads from a .longbow.json file in your project directory that adds info.plist keys, urls to a 1024x1024 icon that it resizes to fit device type. It also handles "
|
12
|
+
spec.summary = "Better target creation for one iOS codebase."
|
13
|
+
spec.homepage = "https://github.com/intermark/longbow"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'bundler', '~> 1.3'
|
22
|
+
spec.add_dependency 'fileutils'
|
23
|
+
spec.add_dependency 'commander', '~> 4.1'
|
24
|
+
spec.add_dependency 'dotenv', '~> 0.7'
|
25
|
+
spec.add_dependency 'mini_magick', '~> 3.7.0'
|
26
|
+
spec.add_dependency 'xcodeproj'
|
27
|
+
spec.add_dependency 'json'
|
28
|
+
|
29
|
+
spec.add_development_dependency "rake"
|
30
|
+
|
31
|
+
spec.files = Dir["./**/*"].reject { |file| file =~ /\.\/(bin|log|pkg|script|spec|test|vendor)/ }
|
32
|
+
spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
33
|
+
spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
34
|
+
spec.require_paths = ["lib"]
|
35
|
+
end
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: longbow
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Intermark Interactive
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: fileutils
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: commander
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: dotenv
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.7'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.7'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mini_magick
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 3.7.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 3.7.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: xcodeproj
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: json
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rake
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: 'Duplicates the main target of an xcodeproj/xcworkspace and renames it,
|
126
|
+
its info.plist, and its image.xcassets as well. It then optionally reads from a
|
127
|
+
.longbow.json file in your project directory that adds info.plist keys, urls to
|
128
|
+
a 1024x1024 icon that it resizes to fit device type. It also handles '
|
129
|
+
email:
|
130
|
+
- interactive@intermarkgroup.com
|
131
|
+
executables:
|
132
|
+
- longbow
|
133
|
+
extensions: []
|
134
|
+
extra_rdoc_files: []
|
135
|
+
files:
|
136
|
+
- ./Gemfile
|
137
|
+
- ./Gemfile.lock
|
138
|
+
- ./lib/longbow/colors.rb
|
139
|
+
- ./lib/longbow/commands/install.rb
|
140
|
+
- ./lib/longbow/commands/shoot.rb
|
141
|
+
- ./lib/longbow/commands.rb
|
142
|
+
- ./lib/longbow/images.rb
|
143
|
+
- ./lib/longbow/plist.rb
|
144
|
+
- ./lib/longbow/targets.rb
|
145
|
+
- ./lib/longbow/version.rb
|
146
|
+
- ./lib/longbow.rb
|
147
|
+
- ./LICENSE.txt
|
148
|
+
- ./longbow-0.0.1.gem
|
149
|
+
- ./longbow.gemspec
|
150
|
+
- ./Rakefile
|
151
|
+
- ./README.md
|
152
|
+
- ./resources/banner.png
|
153
|
+
- bin/longbow
|
154
|
+
homepage: https://github.com/intermark/longbow
|
155
|
+
licenses:
|
156
|
+
- MIT
|
157
|
+
metadata: {}
|
158
|
+
post_install_message:
|
159
|
+
rdoc_options: []
|
160
|
+
require_paths:
|
161
|
+
- lib
|
162
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
168
|
+
requirements:
|
169
|
+
- - '>='
|
170
|
+
- !ruby/object:Gem::Version
|
171
|
+
version: '0'
|
172
|
+
requirements: []
|
173
|
+
rubyforge_project:
|
174
|
+
rubygems_version: 2.1.11
|
175
|
+
signing_key:
|
176
|
+
specification_version: 4
|
177
|
+
summary: Better target creation for one iOS codebase.
|
178
|
+
test_files: []
|