fastlane-plugin-xcconfig_actions 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +38 -0
- data/lib/fastlane/plugin/xcconfig_actions/actions/build_settings_to_flags_action.rb +122 -0
- data/lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb +21 -12
- data/lib/fastlane/plugin/xcconfig_actions/actions/validate_xcconfig_action.rb +5 -1
- data/lib/fastlane/plugin/xcconfig_actions/helper/scripts/parse.py +18 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/scripts/scrape.py +23 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcconfig_actions_helper.rb +10 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspec.rb +259 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.1/Clang LLVM 1.0.xcspec +5888 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.1/CoreBuildSystem.xcspec +4022 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.1/Ld.xcspec +817 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.1/Swift.xcspec +854 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.2/Clang LLVM 1.0.xcspec +6001 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.2/CoreBuildSystem.xcspec +4050 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.2/Ld.xcspec +868 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.2/Swift.xcspec +886 -0
- data/lib/fastlane/plugin/xcconfig_actions/version.rb +1 -1
- metadata +44 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ff5347f5b29ae989604b428568e7a6f6f76a782699a2d6f0afdb1696f9d41ce
|
4
|
+
data.tar.gz: ccabe7e926c527995f7d648916ff5113d510cb9099ea9d312fd49a8db3c08573
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5cf33cdd535fe366170caafe3b6d9a46262dcf9273f14b4c3fa3cebdefd84989d60f59ee59e5e189f394186b5d98666def29db384f753f1e8c62919060dfd66
|
7
|
+
data.tar.gz: 510bed17f4306c2d6aaac5c48992554846ecb19d724574a701a0244fdf32c1fd0d13c3a0baf79bb714459683ee9a643c7f1309ce51353b031185c7c40df75dc9
|
data/README.md
CHANGED
@@ -34,6 +34,41 @@ Things **not supported** at the moment:
|
|
34
34
|
- Use of `<DEVELOPER_DIR>` in include paths
|
35
35
|
- Use of curly braces in variable references, e.g. `${VAR}`
|
36
36
|
|
37
|
+
The build settings are also saved as a dictionary under `SharedValues::XCCONFIG_ACTIONS_BUILD_SETTINGS` key in current `lane_context`.
|
38
|
+
|
39
|
+
### build_settings_to_flags
|
40
|
+
|
41
|
+
Map build settings to Clang Cxx/Objective-C compiler, Swift compiler and Linker flags.
|
42
|
+
|
43
|
+
This action is useful when you plan to reuse xcconfigs with other tools, such as [Buck](https://buckbuild.com/), and you want to translate xcconfigs into the compiler/linker flags.
|
44
|
+
|
45
|
+
Build flags can be printed to standard output or saved to file.
|
46
|
+
The flags are also available via lane context as `lane_context[SharedValues::XCCONFIG_ACTIONS_BUILD_FLAGS]`.
|
47
|
+
|
48
|
+
The result is a dictionary with following keys:
|
49
|
+
|
50
|
+
- `compiler_flags` for Clang CXX/Objective-C compiler.
|
51
|
+
- `swift_compiler_flags` for Swift compiler.
|
52
|
+
- `linker_flags` for Clang linker.
|
53
|
+
|
54
|
+
<!-- TODO: Add info on how it works. -->
|
55
|
+
|
56
|
+
References:
|
57
|
+
|
58
|
+
- [Xcode Build Settings](https://help.apple.com/xcode/mac/10.2/#/itcaec37c2a6)
|
59
|
+
- [LLVM Clang Command Line Options](https://clang.llvm.org/docs/ClangCommandLineReference.html)
|
60
|
+
- [LLVM Clang Diagnostics](https://clang.llvm.org/docs/DiagnosticsReference.html)
|
61
|
+
- [GCC Warning Options](https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html)
|
62
|
+
- https://gist.github.com/fabiopelosin/4560417
|
63
|
+
- https://pewpewthespells.com/blog/xcode_build_system.html
|
64
|
+
|
65
|
+
#### Known Issues
|
66
|
+
|
67
|
+
Flags like `-sdk iphoneos` and `-isysroot iphoneos` may not be suitable for all uses, so may have to remove them from all flags.
|
68
|
+
|
69
|
+
The flag like `-std=gnu++14` is added to `compiler_flags` but it's not applicable for Objective-C code.
|
70
|
+
Most tools have differentiation between C flags (C and Objective-C) and Cxx flags (C++/Objective-C++).
|
71
|
+
|
37
72
|
### validate_xcconfig
|
38
73
|
|
39
74
|
Validate xcconfig using set of very opinionated rules:
|
@@ -96,6 +131,9 @@ Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plu
|
|
96
131
|
# Read xcconfig example.
|
97
132
|
bundle exec fastlane read
|
98
133
|
|
134
|
+
# Map build settings to build flags.
|
135
|
+
bundle exec fastlane build_flags
|
136
|
+
|
99
137
|
# Validate xcconfig example.
|
100
138
|
bundle exec fastlane validate
|
101
139
|
```
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'fastlane/action'
|
2
|
+
require_relative '../helper/xcconfig_actions_helper'
|
3
|
+
require_relative '../helper/xcspec'
|
4
|
+
|
5
|
+
module Fastlane
|
6
|
+
module Actions
|
7
|
+
ActionHelper = Fastlane::Helper::XcconfigActionsHelper
|
8
|
+
Xcspec = Fastlane::Helper::Xcspec
|
9
|
+
|
10
|
+
module SharedValues
|
11
|
+
XCCONFIG_ACTIONS_BUILD_FLAGS = :XCCONFIG_ACTIONS_BUILD_FLAGS
|
12
|
+
end
|
13
|
+
|
14
|
+
class BuildSettingsToFlagsAction < Action
|
15
|
+
def self.run(params)
|
16
|
+
build_settings = params[:build_settings] || Actions.lane_context[SharedValues::XCCONFIG_ACTIONS_BUILD_SETTINGS]
|
17
|
+
UI.user_error!("Missing build settings input") unless build_settings
|
18
|
+
xcode = params[:xcode]
|
19
|
+
|
20
|
+
clang_spec = load_tool_spec("Clang*", xcode: xcode)
|
21
|
+
swift_spec = load_tool_spec("Swift*", xcode: xcode)
|
22
|
+
linker_spec = load_tool_spec("Ld*", xcode: xcode)
|
23
|
+
|
24
|
+
clang_mapping = clang_spec.map_build_settings(build_settings)
|
25
|
+
swift_mapping = swift_spec.map_build_settings(build_settings)
|
26
|
+
linker_mapping = linker_spec.map_build_settings(build_settings)
|
27
|
+
|
28
|
+
flags = {
|
29
|
+
"compiler_flags" => clang_mapping.flags,
|
30
|
+
"swift_compiler_flags" => swift_mapping.flags,
|
31
|
+
"linker_flags" => [
|
32
|
+
linker_mapping.flags,
|
33
|
+
linker_mapping.linker_flags,
|
34
|
+
clang_mapping.linker_flags,
|
35
|
+
swift_mapping.linker_flags
|
36
|
+
].reject(&:empty?).join(" ")
|
37
|
+
}
|
38
|
+
|
39
|
+
Actions.lane_context[SharedValues::XCCONFIG_ACTIONS_BUILD_FLAGS] = flags
|
40
|
+
|
41
|
+
if params[:output_path]
|
42
|
+
File.open(params[:output_path], "w") { |f| f.puts(flags.to_json) }
|
43
|
+
else
|
44
|
+
return flags
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
###
|
49
|
+
# @!group Xcspecs
|
50
|
+
###
|
51
|
+
|
52
|
+
def self.load_tool_spec(tool, xcode:)
|
53
|
+
core_spec_path = ActionHelper.find_xcspec("CoreBuildSystem*", xcode: xcode)
|
54
|
+
standard_spec = Xcspec.new(core_spec_path, id: "com.apple.buildsettings.standard")
|
55
|
+
core_spec = Xcspec.new(core_spec_path, id: "com.apple.build-system.core")
|
56
|
+
|
57
|
+
Xcspec.new(
|
58
|
+
ActionHelper.find_xcspec(tool, xcode: xcode),
|
59
|
+
standard_spec: standard_spec,
|
60
|
+
core_spec: core_spec
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
###
|
65
|
+
# @!group Info and Options
|
66
|
+
###
|
67
|
+
|
68
|
+
def self.description
|
69
|
+
"Map xcconfig build settings to compiler and linker build flags"
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.authors
|
73
|
+
["Maksym Grebenets"]
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.return_value
|
77
|
+
"Build flags dictionary"
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.details
|
81
|
+
[
|
82
|
+
"Build flags keys:",
|
83
|
+
"- compiler_flags: CXX compiler flags for clang compiler",
|
84
|
+
"- swift_compiler_flags: Compiler flags for Swift compiler",
|
85
|
+
"- linker_flags: Linker flags for clang linker (Cxx and Swift)"
|
86
|
+
].join("\n")
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.category
|
90
|
+
:building
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.available_options
|
94
|
+
[
|
95
|
+
FastlaneCore::ConfigItem.new(key: :build_settings,
|
96
|
+
env_name: "XCCONFIG_ACTIONS_BUILD_FLAGS_BUILD_SETTINGS",
|
97
|
+
description: "Build settings to convert to build flags",
|
98
|
+
optional: true,
|
99
|
+
type: Hash,
|
100
|
+
verify_block: proc do |value|
|
101
|
+
UI.user_error!("Missing build settings") if value.nil?
|
102
|
+
end),
|
103
|
+
FastlaneCore::ConfigItem.new(key: :xcode,
|
104
|
+
env_name: "XCCONFIG_ACTIONS_BUILD_FLAGS_XCODE",
|
105
|
+
description: "Xcode version of path to Xcode.app",
|
106
|
+
optional: true,
|
107
|
+
default_value: "10.2",
|
108
|
+
type: String),
|
109
|
+
FastlaneCore::ConfigItem.new(key: :output_path,
|
110
|
+
env_name: "XCCONFIG_ACTIONS_BUILD_FLAGS_OUTPUT_PATH",
|
111
|
+
description: "Output path to save build settings JSON",
|
112
|
+
optional: true,
|
113
|
+
type: String)
|
114
|
+
]
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.is_supported?(platform)
|
118
|
+
[:ios, :mac].include?(platform)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -4,6 +4,10 @@ require_relative '../helper/xcconfig_actions_helper'
|
|
4
4
|
|
5
5
|
module Fastlane
|
6
6
|
module Actions
|
7
|
+
module SharedValues
|
8
|
+
XCCONFIG_ACTIONS_BUILD_SETTINGS = :XCCONFIG_ACTIONS_BUILD_SETTINGS
|
9
|
+
end
|
10
|
+
|
7
11
|
class ReadXcconfigAction < Action
|
8
12
|
def self.run(params)
|
9
13
|
path = params[:path]
|
@@ -13,9 +17,7 @@ module Fastlane
|
|
13
17
|
|
14
18
|
config = read_config(path)
|
15
19
|
|
16
|
-
|
17
|
-
json = config.to_json
|
18
|
-
else
|
20
|
+
unless params[:no_resolve]
|
19
21
|
parent_config = read_config(parent)
|
20
22
|
|
21
23
|
parent_config["SRCROOT"] = srcroot
|
@@ -24,20 +26,22 @@ module Fastlane
|
|
24
26
|
if Helper::XcconfigActionsHelper.command_exist?("xcodebuild")
|
25
27
|
# Set value of XCODE_VERSION_MAJOR not available when reading xcconfigs directly.
|
26
28
|
xcode_version = `xcodebuild -version | head -n1 | cut -d' ' -f2 | xargs`.strip
|
27
|
-
|
28
|
-
parent_config["XCODE_VERSION_MAJOR"] =
|
29
|
+
xcode_version_major_padded = xcode_version.split(".").first.rjust(2, "0") + "00"
|
30
|
+
parent_config["XCODE_VERSION_MAJOR"] = xcode_version_major_padded
|
29
31
|
end
|
30
32
|
|
31
33
|
resolved_parent_config = resolve_config(parent_config)
|
32
34
|
resolved_config = resolve_config(config, parent: resolved_parent_config)
|
33
35
|
|
34
|
-
|
36
|
+
config = resolved_parent_config.merge(resolved_config)
|
35
37
|
end
|
36
38
|
|
39
|
+
Actions.lane_context[SharedValues::XCCONFIG_ACTIONS_BUILD_SETTINGS] = config
|
40
|
+
|
37
41
|
if params[:output_path]
|
38
|
-
File.open(params[:output_path], "w") { |f| f.puts(
|
42
|
+
File.open(params[:output_path], "w") { |f| f.puts(config.to_json) }
|
39
43
|
else
|
40
|
-
return
|
44
|
+
return config
|
41
45
|
end
|
42
46
|
end
|
43
47
|
|
@@ -96,6 +100,7 @@ module Fastlane
|
|
96
100
|
def self.resolve_value(value, key:, resolved: {}, parent: {})
|
97
101
|
matches = value.scan(/(\$\([^$\)]*\))/)
|
98
102
|
|
103
|
+
mutable_value = value.dup # Prevent unwanted side-effect of input modification.
|
99
104
|
matches.each do |group|
|
100
105
|
group.each do |match|
|
101
106
|
var_name = match.delete("$()")
|
@@ -105,13 +110,13 @@ module Fastlane
|
|
105
110
|
else
|
106
111
|
resolved[var_name] || parent[var_name]
|
107
112
|
end
|
108
|
-
|
109
|
-
resolved[key] =
|
113
|
+
mutable_value.gsub!(match, var_value || "")
|
114
|
+
resolved[key] = mutable_value
|
110
115
|
end
|
111
116
|
end
|
112
117
|
|
113
118
|
# If there are still variables, keep resolving then.
|
114
|
-
|
119
|
+
mutable_value.include?("$(") ? resolve_value(mutable_value, key: key, resolved: resolved, parent: parent) : mutable_value
|
115
120
|
end
|
116
121
|
|
117
122
|
# Resolve xcconfig values using parent config.
|
@@ -147,6 +152,10 @@ module Fastlane
|
|
147
152
|
""
|
148
153
|
end
|
149
154
|
|
155
|
+
def self.category
|
156
|
+
:building
|
157
|
+
end
|
158
|
+
|
150
159
|
def self.available_options
|
151
160
|
[
|
152
161
|
FastlaneCore::ConfigItem.new(key: :path,
|
@@ -190,7 +199,7 @@ module Fastlane
|
|
190
199
|
end
|
191
200
|
|
192
201
|
def self.is_supported?(platform)
|
193
|
-
|
202
|
+
[:ios, :mac].include?(platform)
|
194
203
|
end
|
195
204
|
end
|
196
205
|
end
|
@@ -91,6 +91,10 @@ module Fastlane
|
|
91
91
|
].join("\n")
|
92
92
|
end
|
93
93
|
|
94
|
+
def self.category
|
95
|
+
:linting
|
96
|
+
end
|
97
|
+
|
94
98
|
def self.available_options
|
95
99
|
[
|
96
100
|
FastlaneCore::ConfigItem.new(key: :path,
|
@@ -113,7 +117,7 @@ module Fastlane
|
|
113
117
|
end
|
114
118
|
|
115
119
|
def self.is_supported?(platform)
|
116
|
-
|
120
|
+
[:ios, :mac].include?(platform)
|
117
121
|
end
|
118
122
|
end
|
119
123
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from bs4 import BeautifulSoup
|
2
|
+
import re
|
3
|
+
|
4
|
+
soup = BeautifulSoup(open("page.html"), "html.parser")
|
5
|
+
|
6
|
+
entries = soup.find_all("div", id=re.compile("dev.*"))
|
7
|
+
|
8
|
+
title_re = re.compile(".*\((.*)\)")
|
9
|
+
settings = []
|
10
|
+
for e in entries:
|
11
|
+
text = e.find("h2", class_="Name").text
|
12
|
+
m = title_re.match(text)
|
13
|
+
if m == None:
|
14
|
+
settings.append(text)
|
15
|
+
else:
|
16
|
+
settings.append(m.group(1))
|
17
|
+
|
18
|
+
print settings
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# https://stackoverflow.com/questions/8049520/web-scraping-javascript-page-with-python
|
2
|
+
from selenium import webdriver
|
3
|
+
from selenium.webdriver.support.ui import WebDriverWait
|
4
|
+
from selenium.webdriver.support import expected_conditions
|
5
|
+
from selenium.webdriver.common.by import By
|
6
|
+
|
7
|
+
options = webdriver.FirefoxOptions()
|
8
|
+
options.headless = True
|
9
|
+
driver = webdriver.Firefox(firefox_options=options)
|
10
|
+
|
11
|
+
id = "itcaec37c2a6"
|
12
|
+
url = "https://help.apple.com/xcode/mac/10.2/#/{}".format(id)
|
13
|
+
driver.get(url)
|
14
|
+
|
15
|
+
# https://stackoverflow.com/questions/26566799/wait-until-page-is-loaded-with-selenium-webdriver-for-python
|
16
|
+
WebDriverWait(driver, 10).until(
|
17
|
+
expected_conditions.text_to_be_present_in_element(
|
18
|
+
(By.ID, id),
|
19
|
+
'Build settings reference'
|
20
|
+
)
|
21
|
+
)
|
22
|
+
|
23
|
+
print driver.page_source.encode('utf-8').strip()
|
@@ -42,6 +42,16 @@ module Fastlane
|
|
42
42
|
includes: includes
|
43
43
|
}
|
44
44
|
end
|
45
|
+
|
46
|
+
def self.find_xcspec(name, xcode:)
|
47
|
+
search_path = File.exist?(xcode) ? File.join(xcode, "Contents/Plugins") : File.join(File.dirname(__FILE__), "xcspecs", xcode)
|
48
|
+
UI.user_error!("Can't find app path of xcspecs folder for xcode: #{xcode}") unless File.exist?(search_path)
|
49
|
+
|
50
|
+
query = File.join(search_path, "**", name + ".xcspec")
|
51
|
+
xcspec = Dir[query].first
|
52
|
+
UI.user_error!("Can't find xcspec with name: #{name}") unless File.exist?(xcspec)
|
53
|
+
xcspec
|
54
|
+
end
|
45
55
|
end
|
46
56
|
end
|
47
57
|
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
require "plist"
|
2
|
+
require "nokogiri-plist"
|
3
|
+
|
4
|
+
module Fastlane
|
5
|
+
UI = UI unless Fastlane.const_defined?("UI")
|
6
|
+
|
7
|
+
module Helper
|
8
|
+
# Xcspec helper class.
|
9
|
+
class Xcspec
|
10
|
+
###
|
11
|
+
# @!group Mapping
|
12
|
+
###
|
13
|
+
class Mapping
|
14
|
+
attr_reader :flags, :linker_flags
|
15
|
+
|
16
|
+
def initialize(flags = "", linker_flags = "")
|
17
|
+
@flags = flags
|
18
|
+
@linker_flags = linker_flags
|
19
|
+
end
|
20
|
+
|
21
|
+
# Join with other mapping and return new mapping.
|
22
|
+
def join(other)
|
23
|
+
return self if other.nil?
|
24
|
+
|
25
|
+
joined_flags = [flags, other.flags].reject(&:empty?).join(" ")
|
26
|
+
joined_linker_flags = [linker_flags, other.linker_flags].reject(&:empty?).join(" ")
|
27
|
+
|
28
|
+
Mapping.new(joined_flags, joined_linker_flags)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
###
|
33
|
+
# @!group Initialization
|
34
|
+
###
|
35
|
+
|
36
|
+
attr_reader :tool
|
37
|
+
|
38
|
+
def initialize(path, id: nil, standard_spec: nil, core_spec: nil)
|
39
|
+
UI.user_error!("No such file: #{path}") unless path && File.exist?(path)
|
40
|
+
|
41
|
+
@plist = Xcspec.load_plist(path)
|
42
|
+
@tool = find_tool(id)
|
43
|
+
|
44
|
+
all_tools = [@tool]
|
45
|
+
all_tools << standard_spec.tool if standard_spec
|
46
|
+
all_tools << core_spec.tool if core_spec
|
47
|
+
|
48
|
+
@all_options = all_tools.flat_map { |t| t["Options"] }
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.load_plist(path)
|
52
|
+
file_type = `file -b --mime-type #{path.shellescape}`.chomp
|
53
|
+
if file_type == "text/xml" || file_type == "application/xml"
|
54
|
+
xml_plist = path
|
55
|
+
else
|
56
|
+
if FastlaneCore::Helper.mac?
|
57
|
+
xml_plist = Tempfile.new("xcspec.plist").path
|
58
|
+
result = system("plutil -convert xml1 -o #{xml_plist.shellescape} #{path.shellescape}")
|
59
|
+
UI.user_error!("Couldn't convert #{path} xcspec to XML plist") unless result
|
60
|
+
else
|
61
|
+
# There is plist-utils library, but it can only convert binary to XML, can't handle ASCII.
|
62
|
+
UI.user_error!("Can't convert ASCII plists to XML on Linux or Windows platform")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
Nokogiri::PList(File.open(xml_plist))
|
67
|
+
end
|
68
|
+
|
69
|
+
def find_tool(id)
|
70
|
+
return @plist unless @plist.kind_of?(Array)
|
71
|
+
id ? @plist.find { |t| t["Identifier"] == id } : @plist.first
|
72
|
+
end
|
73
|
+
|
74
|
+
###
|
75
|
+
# @!group Mapping
|
76
|
+
###
|
77
|
+
|
78
|
+
def map_build_settings(build_settings)
|
79
|
+
# Build settings provided for mapping will not include all possible build settings.
|
80
|
+
# Add default values for missing build settings.
|
81
|
+
missing_build_settings = @all_options.reduce({}) do |memo, opt|
|
82
|
+
name = opt["Name"]
|
83
|
+
build_settings.key?(name) ? memo : memo.merge({ name => opt["DefaultValue"] })
|
84
|
+
end
|
85
|
+
complete_build_settings = build_settings.merge(missing_build_settings)
|
86
|
+
|
87
|
+
mappings = build_settings.flat_map do |setting, value|
|
88
|
+
map_build_setting_value(setting, value, complete_build_settings)
|
89
|
+
end.compact
|
90
|
+
|
91
|
+
mappings.reduce(Mapping.new) { |memo, m| memo.join(m) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def map_build_setting_value(name, value, build_settings)
|
95
|
+
option = @tool["Options"].find { |o| o["Name"] == name }
|
96
|
+
return nil unless option
|
97
|
+
|
98
|
+
map_option(option, value, build_settings)
|
99
|
+
end
|
100
|
+
|
101
|
+
def map_option(option, value, build_settings)
|
102
|
+
# Evaluate and check the 'Condition'.
|
103
|
+
return nil unless check_condition(option, build_settings)
|
104
|
+
|
105
|
+
# If type of the value is one of List types (StringList, PathList),
|
106
|
+
# then split it into value list, else just use scalar value itself.
|
107
|
+
scalar_values = option["Type"].downcase.end_with?("list") ? value.split : [value]
|
108
|
+
scalar_values.flat_map { |v| map_option_scalar_value(option, v, build_settings) }.compact
|
109
|
+
end
|
110
|
+
|
111
|
+
def map_option_scalar_value(option, value, build_settings)
|
112
|
+
# At this point we deal with scalar value.
|
113
|
+
|
114
|
+
# Type = StringList
|
115
|
+
# - CommandLineFlag and CommandLinePrefixFlag - map each string in the list.
|
116
|
+
# - CommandLineArgs - map each string in the list.
|
117
|
+
# - Finally there are entries with only Category = CustomFlags.
|
118
|
+
# Those end up in compiler flags for sure, so looks like if category is CustomFlags,
|
119
|
+
# then should use them as is.
|
120
|
+
# There's only 3 of them: OTHER_CFLAGS, OTHER_CPLUSPLUSFLAGS and WARNING_CFLAGS.
|
121
|
+
# PathList, that for all intents and purposes can be handed as StringList.
|
122
|
+
|
123
|
+
# - CommandLineArgs
|
124
|
+
# - If input is StringList, then map each entry from string list according to CommandLineArgs value
|
125
|
+
# The value of CommandLineArgs can be an array, use flat map
|
126
|
+
# Can have a switch, e.g.
|
127
|
+
# CommandLineArgs = {
|
128
|
+
# "" = ();
|
129
|
+
# "<<otherwise>>" = (
|
130
|
+
# "-$(DEPLOYMENT_TARGET_CLANG_FLAG_NAME)=$(value)",
|
131
|
+
# );
|
132
|
+
# };
|
133
|
+
# All these switches, however, map to nothing for empty string and to something for non-empty string.
|
134
|
+
# The vey same switch is used for enums. Switching for enums vs string is no different,
|
135
|
+
# since switching happens on string values.
|
136
|
+
# <<otherwise>> is for other values
|
137
|
+
# - AdditionalLinkerArgs
|
138
|
+
# Few build settings have them, collected separately, work just like CommandLineArgs for parsing.
|
139
|
+
# - CommandLineFlag and CommandLinePrefixFlag
|
140
|
+
# - If input is StringList then applied to each entry in the list, to each entry applied as to a scalar value:
|
141
|
+
# - If input is scalar String value, then applied just once as -<flag> $(value)
|
142
|
+
# - If input is boolean, then just the flag is used, e.g. -<flag>, no Prefix option supported for boolean.
|
143
|
+
# - While for prefix it appears there's no space and it is -<prefix>$(value)
|
144
|
+
# - An exception(!) is MACH_O_TYPE, where no value is appended but just the flag is used.
|
145
|
+
# It is also the only enum where list of values has command line flag for each value.
|
146
|
+
|
147
|
+
# Type = Enumeration
|
148
|
+
# Most of them have CommandLineArgs with a switch.
|
149
|
+
# Some also come with AdditionalLinkerArgs, which is mapped in similar way.
|
150
|
+
# Then there's MACH_O_TYPE, which has CommandLineFlag under Values...
|
151
|
+
# But only when used for linker flags:
|
152
|
+
# Value = "mh_dylib";
|
153
|
+
# CommandLineFlag = "-dynamiclib";
|
154
|
+
# -dynamiclib is a flag that doesn't take input, so only specified as is.
|
155
|
+
|
156
|
+
flags = ""
|
157
|
+
if (cli_args = option["CommandLineArgs"])
|
158
|
+
flags = map_args(option, cli_args, value, build_settings)
|
159
|
+
elsif (cli_flag = option["CommandLineFlag"])
|
160
|
+
# If Boolean and YES, then use CommandLineFlag value.
|
161
|
+
if option["Type"] == "Boolean"
|
162
|
+
flags = cli_flag if value == "YES"
|
163
|
+
else
|
164
|
+
flags = [cli_flag, value].join(" ")
|
165
|
+
end
|
166
|
+
elsif (cli_prefix_flag = option["CommandLinePrefixFlag"])
|
167
|
+
flags = [cli_prefix_flag, value].join # Join with no space in between.
|
168
|
+
elsif option["Category"] == "CustomFlags"
|
169
|
+
flags = value
|
170
|
+
else
|
171
|
+
# Nothing to map to, except for when it's MACH_O_TYPE, when it has list of dictionaries:
|
172
|
+
# Values = ( { Value = "v", CommandLineFlag = "f", ... } )
|
173
|
+
match = (option["Values"] || []).find { |v| v["Value"] == value } || {}
|
174
|
+
flags = match["CommandLineFlag"] || ""
|
175
|
+
end
|
176
|
+
|
177
|
+
linker_flags = ""
|
178
|
+
if (linker_args = option["AdditionalLinkerArgs"])
|
179
|
+
linker_flags = map_args(option, linker_args, value, build_settings)
|
180
|
+
end
|
181
|
+
|
182
|
+
Mapping.new(flags, linker_flags)
|
183
|
+
end
|
184
|
+
|
185
|
+
def map_args(option, args, value, build_settings)
|
186
|
+
return "" unless args
|
187
|
+
|
188
|
+
if args.kind_of?(Hash)
|
189
|
+
map_args(option, args[value] || args["<<otherwise>>"], value, build_settings)
|
190
|
+
else
|
191
|
+
# Args should be an array here, but can be just single string.
|
192
|
+
|
193
|
+
resolved_args = args.kind_of?(Array) ? args.dup : [args]
|
194
|
+
# Replacing $(value) is just one part, need to resolve any build settings present too.
|
195
|
+
resolved_args = resolved_args.map { |a| a.gsub("$(value)", value) }.join(" ")
|
196
|
+
Fastlane::Actions::ReadXcconfigAction.resolve_value(
|
197
|
+
resolved_args,
|
198
|
+
key: "resolved_args",
|
199
|
+
resolved: {},
|
200
|
+
parent: build_settings
|
201
|
+
)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Constants for condition evaluation.
|
206
|
+
NO = false
|
207
|
+
YES = true
|
208
|
+
|
209
|
+
# Evaluate and check the condition.
|
210
|
+
# Conditions come in form like this:
|
211
|
+
# "$(COMPILER_INDEX_STORE_ENABLE) == YES || ( $(COMPILER_INDEX_STORE_ENABLE) == Default && $(GCC_OPTIMIZATION_LEVEL) == 0 )"
|
212
|
+
# Return true if there's no condition to evaluate.
|
213
|
+
def check_condition(option, build_settings)
|
214
|
+
condition = option["Condition"]
|
215
|
+
return true unless condition
|
216
|
+
|
217
|
+
# Need to resolve all $(VAR) references using build settings.
|
218
|
+
# At this point using complete build settings,
|
219
|
+
# which include default values for all known build settings.
|
220
|
+
|
221
|
+
# Handy that read_xcconfig action already has a helper to resolve a value.
|
222
|
+
# Just pass current build settings as parent config.
|
223
|
+
resolved_condition = Fastlane::Actions::ReadXcconfigAction.resolve_value(
|
224
|
+
condition,
|
225
|
+
key: "condition",
|
226
|
+
resolved: {},
|
227
|
+
parent: build_settings
|
228
|
+
)
|
229
|
+
|
230
|
+
# Resolved condition is now a C-like expression, which also can be evaluated as Ruby code.
|
231
|
+
# With small changes though.
|
232
|
+
# All literals in condition can be treated as strings, except for YES/NO boolean literals,
|
233
|
+
# which are replaced with `true` and `false`.
|
234
|
+
# However, xcspecs are very inconsistent when it comes to strings.
|
235
|
+
# Some values are used unquoted in the conditions, such as:
|
236
|
+
# Defatult, mh_object, bitcode.
|
237
|
+
# There's also use of '' and \"\" for empty string, the latter may cause issues.
|
238
|
+
# Then "same-as-input" that may have to be resolved - do not handle for now.
|
239
|
+
# Finally, $(variant) == profile - just leave it for now.
|
240
|
+
|
241
|
+
# Ways to fix.
|
242
|
+
# 1. Scan for all enums in xcspecs and define module vars for each enum value,
|
243
|
+
# so when evaluated, is replaced with variable.
|
244
|
+
# 2. Process resolved condition by wrapping all unwrapped entries in quotes.
|
245
|
+
# Using approach 2 for now with insane regex.
|
246
|
+
|
247
|
+
# In all cases replace \"\" with ''.
|
248
|
+
resolved_condition.gsub!('"', "'")
|
249
|
+
|
250
|
+
# Quote everything except YES and NO.
|
251
|
+
resolved_condition = (" " + resolved_condition + " ").gsub(/\s((\w|\d|-|\+|\.)+?)\s/, " '\\1' ").gsub(/'(YES|NO)'/, '\\1')
|
252
|
+
|
253
|
+
# rubocop:disable Security/Eval
|
254
|
+
eval(resolved_condition)
|
255
|
+
# rubocop:enable Security/Eval
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|