xcode-archive-cache 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,45 @@
1
+ module XcodeArchiveCache
2
+ class Command < CLAide::Command
3
+
4
+ include XcodeArchiveCache::Logs
5
+
6
+ self.abstract_command = true
7
+ self.command = "xcode-archive-cache"
8
+ self.description = "XcodeArchiveCache, cache for Xcode archive action"
9
+
10
+ def self.options
11
+ [
12
+ ["--log-level=[nothing|info|verbose]", "Level of log verbosity, defaults to info"]
13
+ ].concat(super.reject { |(name, _)| name != "--help" })
14
+ end
15
+
16
+ def self.run(argv)
17
+ help! "You cannot run XcodeArchiveCache as root." if Process.uid == 0 && !Gem.win_platform?
18
+
19
+ super(argv)
20
+ end
21
+
22
+ def initialize(argv)
23
+ super
24
+ log_level = argv.option("log-level", "info")
25
+ set_log_level(log_level)
26
+ end
27
+ end
28
+
29
+ def self.report_error(exception)
30
+ case exception
31
+ when Interrupt
32
+ info("Cancelled")
33
+ exit 1
34
+ when SystemExit
35
+ raise
36
+ else
37
+ if ENV["XCODE_ARCHIVE_CACHE_ENV"] != "development"
38
+ error(exception.message)
39
+ exit 1
40
+ else
41
+ raise exception
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,52 @@
1
+ module XcodeArchiveCache
2
+ class Command
3
+ class Inject < Command
4
+
5
+ include XcodeArchiveCache::Logs
6
+
7
+ self.description = "Setup workspace or project for being built using cached dependencies"
8
+
9
+ def self.options
10
+ [
11
+ ["--cachefile=/path/to/Cachefile", "Explicit Cachefile location, defaults to $PWD/Cachefile"],
12
+ ["--configuration", "Name of configuration from Cachefile, required"],
13
+ ["--destination=[<full destination specifier>|generic]", "xcodebuild-style destination. Defaults to 'generic', ignored for archive action"],
14
+ ["--storage=/path/to/storage", "Storage dir path, required"]
15
+ ].concat(super)
16
+ end
17
+
18
+ DEFAULT_CACHEFILE = "Cachefile"
19
+
20
+ def initialize(argv)
21
+ super
22
+
23
+ @configuration_name = argv.option("configuration")
24
+ if @configuration_name == nil
25
+ error("configuration is required")
26
+ exit 1
27
+ end
28
+
29
+ @storage = argv.option("storage")
30
+ if @storage == nil
31
+ error("storage parameter is required")
32
+ exit 1
33
+ end
34
+
35
+ @cachefile_path = argv.option("cachefile", DEFAULT_CACHEFILE)
36
+ @destination = argv.option("destination", XcodeArchiveCache::Xcodebuild::GENERIC_DESTINATION)
37
+ end
38
+
39
+ def run
40
+ config = XcodeArchiveCache::Config.from_file(@cachefile_path).entry
41
+ config.settings.destination = @destination
42
+ config.active_configuration_name = @configuration_name
43
+ config.storage.type = :local
44
+ config.storage.path = @storage
45
+
46
+ debug(config)
47
+
48
+ XcodeArchiveCache::Runner.new(config).run
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,211 @@
1
+ module XcodeArchiveCache
2
+ class Config
3
+
4
+ include XcodeArchiveCache::Config::DSL
5
+
6
+ class Entry
7
+
8
+ # @return [String]
9
+ #
10
+ attr_reader :name
11
+
12
+ # @return [Array<Configuration>]
13
+ #
14
+ attr_reader :configurations
15
+
16
+ # @return [String]
17
+ #
18
+ attr_accessor :active_configuration_name
19
+
20
+ # @return [Array<Target>]
21
+ #
22
+ attr_reader :targets
23
+
24
+ # @return [Settings]
25
+ #
26
+ attr_reader :settings
27
+
28
+ # @return [Storage]
29
+ #
30
+ attr_reader :storage
31
+
32
+ def initialize(name)
33
+ @name = name
34
+ @configurations = []
35
+ @active_configuration_name = nil
36
+ @settings = Settings.new
37
+ @storage = Storage.new
38
+ @targets = []
39
+ @file_extname = ""
40
+ end
41
+
42
+ # @return [String]
43
+ #
44
+ def file_path
45
+ return name if File.extname(name) == file_extname
46
+
47
+ name + file_extname
48
+ end
49
+
50
+ # @return [Configuration]
51
+ #
52
+ def active_configuration
53
+ configuration = configurations.select{|config| config.name == active_configuration_name }.first
54
+ if configuration == nil
55
+ raise Informative, "Found no configuration with name \"#{active_configuration_name}\""
56
+ end
57
+
58
+ configuration
59
+ end
60
+
61
+ def to_s
62
+ "path: #{file_path}\nactive configuration: #{active_configuration_name}\nconfigurations:\n\t#{configurations.join("\n\t")}\n#{settings}\nstorage: #{storage}\ntargets:\n\t#{targets.join("\n\t")}"
63
+ end
64
+
65
+ private
66
+
67
+ # @return [String]
68
+ #
69
+ attr_reader :file_extname
70
+ end
71
+
72
+ class Workspace < Entry
73
+ def initialize(path)
74
+ super(path)
75
+ @file_extname = ".xcworkspace"
76
+ end
77
+ end
78
+
79
+ class Project < Entry
80
+ def initialize(path)
81
+ super(path)
82
+ @file_extname = ".xcodeproj"
83
+ end
84
+ end
85
+
86
+ class Configuration
87
+ # @return [String]
88
+ #
89
+ attr_reader :name
90
+
91
+ # @return [String]
92
+ #
93
+ attr_accessor :build_configuration
94
+
95
+ # @return [String]
96
+ #
97
+ attr_accessor :action
98
+
99
+ # @return [String]
100
+ #
101
+ attr_accessor :xcodebuild_args
102
+
103
+ # @param [String] name
104
+ #
105
+ def initialize(name)
106
+ @name = name
107
+ @action = "archive"
108
+ end
109
+
110
+ def to_s
111
+ "#{name}, build configuration: #{build_configuration}, action: #{action}, xcodebuild args: \"#{xcodebuild_args}\""
112
+ end
113
+ end
114
+
115
+ class Settings
116
+ # @return [String]
117
+ #
118
+ attr_accessor :derived_data_path
119
+
120
+ # @return [String]
121
+ #
122
+ attr_accessor :destination
123
+
124
+ def to_s
125
+ "destination: #{destination}, derived data path: #{derived_data_path}"
126
+ end
127
+ end
128
+
129
+ class Storage
130
+
131
+ # @return [Symbol]
132
+ #
133
+ attr_accessor :type
134
+
135
+ # @return [String]
136
+ #
137
+ attr_accessor :path
138
+
139
+ def to_s
140
+ "#{type}, path: #{path}"
141
+ end
142
+ end
143
+
144
+ class Target
145
+
146
+ # @return [String]
147
+ #
148
+ attr_reader :name
149
+
150
+ # @return [Array<String>]
151
+ #
152
+ attr_reader :dependencies
153
+
154
+ # @param [String] name
155
+ #
156
+ def initialize(name)
157
+ @name = name
158
+ @dependencies = []
159
+ end
160
+
161
+ def to_s
162
+ "#{name}, dependencies: #{dependencies.join(", ")}"
163
+ end
164
+ end
165
+
166
+ # @param [String] path
167
+ #
168
+ def self.from_file(path)
169
+ contents = File.open(path, "r:utf-8", &:read)
170
+
171
+ config = Config.new do
172
+ begin
173
+ eval(contents, nil, path)
174
+ rescue Exception => e
175
+ raise Informative, "Invalid #{File.basename(path)} file: #{e.message}"
176
+ end
177
+ end
178
+
179
+ config
180
+ end
181
+
182
+ # @return [Entry]
183
+ #
184
+ attr_reader :entry
185
+
186
+ def initialize(&block)
187
+ @entry = nil
188
+ @current_target = nil
189
+
190
+ if block
191
+ instance_eval(&block)
192
+ end
193
+ end
194
+
195
+ private
196
+
197
+ attr_writer :entry
198
+
199
+ # @return [Configuration]
200
+ #
201
+ def current_configuration
202
+ entry.configurations.last
203
+ end
204
+
205
+ # @return [Target]
206
+ #
207
+ def current_target
208
+ entry.targets.last
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,69 @@
1
+ module XcodeArchiveCache
2
+ class Config
3
+ module DSL
4
+
5
+ # @param [String] name
6
+ #
7
+ def workspace(name)
8
+ self.entry = Workspace.new(name)
9
+
10
+ yield
11
+ end
12
+
13
+ # @param [String] name
14
+ #
15
+ def project(name)
16
+ self.entry = Project.new(name)
17
+
18
+ yield
19
+ end
20
+
21
+ # @param [String] name
22
+ #
23
+ def configuration(name)
24
+ entry.configurations.push(Configuration.new(name))
25
+
26
+ yield
27
+ end
28
+
29
+ # @param [String] name
30
+ #
31
+ def build_configuration(name)
32
+ current_configuration.build_configuration = name
33
+ end
34
+
35
+ # @param [String] name
36
+ #
37
+ def action(name)
38
+ current_configuration.action = name
39
+ end
40
+
41
+ # @param [String] args
42
+ #
43
+ def xcodebuild_args(args)
44
+ current_configuration.xcodebuild_args = args
45
+ end
46
+
47
+ # @param [String] path
48
+ #
49
+ def derived_data_path(path)
50
+ entry.settings.derived_data_path = path
51
+ end
52
+
53
+ # @return [String]
54
+ #
55
+ def target(name)
56
+ target = Target.new(name)
57
+ entry.targets.push(target)
58
+
59
+ yield
60
+ end
61
+
62
+ # @param [String] name
63
+ #
64
+ def cache(name)
65
+ current_target.dependencies.push(name)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,172 @@
1
+ module XcodeArchiveCache
2
+ module Injection
3
+ class BuildFlagsChanger
4
+
5
+ include XcodeArchiveCache::Logs
6
+
7
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
8
+ # @param [String] path
9
+ #
10
+ def add_framework_search_path(build_configuration, path)
11
+ debug("using framework search path #{path}")
12
+ add_flag_to_configuration(build_configuration, FRAMEWORK_SEARCH_PATHS_KEY, path_to_search_path(path))
13
+ end
14
+
15
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
16
+ # @param [XcodeArchiveCache::BuildGraph::Node] node
17
+ #
18
+ def add_framework_linker_flag(build_configuration, node)
19
+ linker_flag = get_framework_linker_flag(node)
20
+ if linker_flag
21
+ debug("using linker flag #{linker_flag}")
22
+ add_linker_flag(build_configuration, linker_flag)
23
+ end
24
+ end
25
+
26
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
27
+ # @param [XcodeArchiveCache::BuildGraph::Node] node
28
+ #
29
+ def add_framework_headers_iquote(build_configuration, artifact_location, node)
30
+ headers_search_path = get_framework_headers_iquote(artifact_location, node)
31
+ debug("using -iquote path #{headers_search_path}")
32
+ add_cflag(build_configuration, headers_search_path)
33
+ end
34
+
35
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
36
+ # @param [String] path
37
+ #
38
+ def add_library_search_path(build_configuration, path)
39
+ debug("using library search path #{path}")
40
+ add_flag_to_configuration(build_configuration, LIBRARY_SEARCH_PATHS_KEY, path_to_search_path(path))
41
+ end
42
+
43
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
44
+ # @param [String] path
45
+ #
46
+ def add_headers_search_path(build_configuration, path)
47
+ debug("using headers search path #{path}")
48
+ add_flag_to_configuration(build_configuration, HEADER_SEARCH_PATHS_KEY, path)
49
+ end
50
+
51
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
52
+ # @param [String] path
53
+ #
54
+ def add_iquote_path(build_configuration, path)
55
+ debug("using -iquote path #{path}")
56
+ add_cflag(build_configuration, path_to_iquote(path))
57
+ end
58
+
59
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
60
+ # @param [String] path
61
+ #
62
+ def add_capital_i_path(build_configuration, path)
63
+ debug("using -I path #{path}")
64
+ add_cflag(build_configuration, path_to_capital_i(path))
65
+ end
66
+
67
+ private
68
+
69
+ FRAMEWORK_SEARCH_PATHS_KEY = "FRAMEWORK_SEARCH_PATHS"
70
+ LIBRARY_SEARCH_PATHS_KEY = "LIBRARY_SEARCH_PATHS"
71
+ HEADER_SEARCH_PATHS_KEY = "HEADER_SEARCH_PATHS"
72
+ OTHER_CFLAGS_KEY = "OTHER_CFLAGS"
73
+ OTHER_LDFLAGS_KEY = "OTHER_LDFLAGS"
74
+ INHERITED_SETTINGS_VALUE = "$(inherited)"
75
+
76
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
77
+ # @param [String] flag
78
+ #
79
+ def add_linker_flag(build_configuration, flag)
80
+ add_flag_to_configuration(build_configuration, OTHER_LDFLAGS_KEY, flag)
81
+ end
82
+
83
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
84
+ # @param [String] flag
85
+ #
86
+ def add_cflag(build_configuration, flag)
87
+ add_flag_to_configuration(build_configuration, OTHER_CFLAGS_KEY, flag)
88
+ end
89
+
90
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
91
+ # @param [String] key
92
+ # @param [String] flag
93
+ #
94
+ def add_flag_to_configuration(build_configuration, key, flag)
95
+ flags = build_configuration.build_settings[key]
96
+ build_configuration.build_settings[key] = add_flag(flags, flag)
97
+ end
98
+
99
+ # @param [Object] flags
100
+ # @param [String] new_flag
101
+ #
102
+ # @return [Array<String>]
103
+ #
104
+ def add_flag(flags, new_flag)
105
+ if flags && flags.length > 0
106
+ if flags.is_a?(String)
107
+ flags = [flags, new_flag]
108
+ elsif flags.is_a?(Array)
109
+ flags += [new_flag] unless flags.include?(new_flag)
110
+ else
111
+ raise StandardError.new, "Flags value is neither string nor array: #{flags.class}"
112
+ end
113
+ else
114
+ flags = [INHERITED_SETTINGS_VALUE, new_flag]
115
+ end
116
+
117
+ flags
118
+ end
119
+
120
+ # @param [String] path
121
+ #
122
+ # @return [String]
123
+ #
124
+ def path_to_search_path(path)
125
+ "\"#{path}\""
126
+ end
127
+
128
+ # @param [String] path
129
+ # @param [XcodeArchiveCache::BuildGraph::Node] node
130
+ #
131
+ # @return [String]
132
+ #
133
+ def get_framework_headers_iquote(path, node)
134
+ if node.has_framework_product?
135
+ headers_dir_path = File.join(path, node.product_file_name, "Headers")
136
+ path_to_iquote(headers_dir_path)
137
+ end
138
+ end
139
+
140
+ # @param [String] path
141
+ #
142
+ # @return [String]
143
+ #
144
+ def path_to_iquote(path)
145
+ "-iquote \"#{path}\""
146
+ end
147
+
148
+ # @param [String] path
149
+ #
150
+ # @return [Stirng]
151
+ #
152
+ def path_to_capital_i(path)
153
+ "-I\"#{path}\""
154
+ end
155
+
156
+ # @param [XcodeArchiveCache::BuildGraph::Node] node
157
+ #
158
+ # @return [String]
159
+ #
160
+ # Something.framework -> -framework "Something"
161
+ #
162
+ def get_framework_linker_flag(node)
163
+ return unless node.product_file_name
164
+
165
+ framework_name = File.basename(node.product_file_name, File.extname(node.product_file_name))
166
+ return unless framework_name
167
+
168
+ "-framework \"#{framework_name}\""
169
+ end
170
+ end
171
+ end
172
+ end