nixenvironment 0.0.59 → 0.0.60
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/README.md +14 -14
- data/bin/Config +2 -2
- data/bin/nixenvironment +420 -842
- data/legacy/CleanWorkingCopy.sh +69 -0
- data/legacy/Deploy.sh +44 -0
- data/legacy/DeployAPK.py +58 -0
- data/legacy/DeployIPA.sh +125 -0
- data/legacy/DetectSCM.sh +11 -0
- data/legacy/GenerateCodeCoverageForXCTests.sh +134 -0
- data/legacy/GenerateCodeDuplicationReport.sh +24 -0
- data/legacy/IncrementBuildNumber.py +129 -0
- data/legacy/LoadBuildEnvVars.sh +116 -0
- data/legacy/MakeTag.sh +94 -0
- data/legacy/RemoveTemporaryFiles.sh +9 -0
- data/legacy/SaveRevision.sh +122 -0
- data/legacy/UnityBuildAndroid.py +84 -0
- data/legacy/UnityBuildAutomationScripts/CommandLineReader.cs +130 -0
- data/legacy/UnityBuildAutomationScripts/NIXBuilder.cs +105 -0
- data/legacy/UnityBuildEnvVars.py +41 -0
- data/legacy/VerifyBinarySigning.py +80 -0
- data/legacy/svn-clean.pl +246 -0
- data/legacy/svncopy.pl +1134 -0
- data/lib/nixenvironment.rb +5 -0
- data/lib/nixenvironment/archiver.rb +690 -0
- data/lib/nixenvironment/build_env_vars_loader.rb +24 -0
- data/lib/nixenvironment/cmd_executor.rb +33 -0
- data/lib/nixenvironment/config.rb +127 -14
- data/lib/nixenvironment/git.rb +107 -0
- data/lib/nixenvironment/plist.rb +52 -0
- data/lib/nixenvironment/version.rb +1 -1
- data/lib/nixenvironment/xcodebuild.rb +167 -0
- data/nixenvironment.gemspec +6 -0
- data/utils/XcodeIconTagger/IconTagger +0 -0
- data/utils/XcodeIconTagger/masks/OneLineMask.png +0 -0
- data/utils/XcodeIconTagger/masks/TwoLineMask.png +0 -0
- data/utils/aapt +0 -0
- data/utils/gcovr +986 -0
- data/utils/identitieslist +0 -0
- data/utils/simian-2.3.33.jar +0 -0
- metadata +118 -2
@@ -0,0 +1,24 @@
|
|
1
|
+
module Nixenvironment
|
2
|
+
class BuildEnvVarsLoader
|
3
|
+
def self.load
|
4
|
+
cmd_output = %x[ source \"#{LOAD_BUILD_ENV_VARS_SCRIPT_PATH}\" ].strip!
|
5
|
+
build_env_vars = {}
|
6
|
+
|
7
|
+
vars_list = cmd_output.split(/\n/).reject(&:empty?)
|
8
|
+
if vars_list.present?
|
9
|
+
vars_to_strip = Hash[vars_list.map { |it| it.split('=', 2) }]
|
10
|
+
vars_to_strip.each do |key, value|
|
11
|
+
if key.present? && value.present?
|
12
|
+
stripped_key = key.strip
|
13
|
+
stripped_value = value.strip
|
14
|
+
|
15
|
+
puts "#{stripped_key} : #{stripped_value}"
|
16
|
+
build_env_vars[stripped_key] = stripped_value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
build_env_vars
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Nixenvironment
|
2
|
+
class CmdExecutor
|
3
|
+
@binary_name = nil
|
4
|
+
|
5
|
+
@last_cmd_success = nil
|
6
|
+
@last_cmd_exitstatus = nil
|
7
|
+
|
8
|
+
def self.last_cmd_success?
|
9
|
+
@last_cmd_success
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.last_cmd_exitstatus
|
13
|
+
@last_cmd_exitstatus
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.execute(cmd, opts = [], silent = false)
|
17
|
+
input = "#{@binary_name}"
|
18
|
+
input << " #{cmd}" if cmd.present?
|
19
|
+
input << " #{opts.join(' ')}" if opts.present?
|
20
|
+
output = ''
|
21
|
+
|
22
|
+
IO.popen(input).each do |line|
|
23
|
+
puts line unless silent
|
24
|
+
output << line
|
25
|
+
end.close
|
26
|
+
|
27
|
+
@last_cmd_success = $?.success?
|
28
|
+
@last_cmd_exitstatus = $?.exitstatus
|
29
|
+
|
30
|
+
output
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,23 +1,136 @@
|
|
1
1
|
module Nixenvironment
|
2
|
-
|
3
|
-
|
2
|
+
# template peojects repositories
|
3
|
+
TEMPLATES_REPO_LIST = {
|
4
|
+
'objc' => 'https://bitbucket.org/nixsolutions/np.git',
|
5
|
+
'swift' => 'https://bitbucket.org/nixsolutions/nps.git',
|
6
|
+
'unity' => 'https://bitbucket.org/nixsolutions/nup.git'
|
7
|
+
}
|
8
|
+
|
9
|
+
# config
|
10
|
+
CONFIG_SETTINGS_FILE_NAME = 'Config'
|
11
|
+
|
12
|
+
# ninbas legacy
|
13
|
+
BUILD_SCRIPTS_PATH = File.expand_path('../../../legacy', __FILE__)
|
14
|
+
SAVE_REVISION_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'SaveRevision.sh')
|
15
|
+
UNITY_BUILD_ANDROID_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'UnityBuildAndroid.py')
|
16
|
+
LOAD_BUILD_ENV_VARS_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'LoadBuildEnvVars.sh')
|
17
|
+
REMOVE_TEMPORARY_FILES_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'RemoveTemporaryFiles.sh')
|
18
|
+
CODE_COVERAGE_REPORT_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'GenerateCodeCoverageForXCTests.sh')
|
19
|
+
CODE_DUPLICATION_REPORT_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'GenerateCodeDuplicationReport.sh')
|
20
|
+
MAKE_TAG_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'MakeTag.sh')
|
21
|
+
CLEAN_WORKING_COPY_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'CleanWorkingCopy.sh')
|
22
|
+
DEPLOY_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'Deploy.sh')
|
23
|
+
DEPLOY_IPA_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'DeployIPA.sh')
|
24
|
+
DEPLOY_APK_SCRIPT_PATH = File.join(BUILD_SCRIPTS_PATH, 'DeployAPK.py')
|
25
|
+
|
26
|
+
# utils
|
27
|
+
UTILS_PATH = File.expand_path('../../../utils', __FILE__)
|
28
|
+
IDENTITIESLIST_UTILITY_PATH = File.join(UTILS_PATH, 'identitieslist')
|
29
|
+
TAGGER_UTILITY_DIRECTORY = File.join(UTILS_PATH, 'XcodeIconTagger')
|
30
|
+
TAGGER_UTILITY_PATH = File.join(TAGGER_UTILITY_DIRECTORY, 'IconTagger')
|
31
|
+
|
32
|
+
# macos
|
33
|
+
MACOS_PROJECTS_DEPLOY_PATH = 'Projects/macOSProjects'
|
34
|
+
|
35
|
+
# file extensions
|
36
|
+
APP_EXT = '.app'
|
37
|
+
APPEX_EXT = '.appex'
|
38
|
+
DSYM_EXT = '.dSYM'
|
39
|
+
ZIP_EXT = '.zip'
|
40
|
+
GIT_EXT = '.git'
|
41
|
+
|
42
|
+
# watch, extensions, widgets
|
43
|
+
WATCHKIT_APP_KEY = 'WatchKit App'
|
44
|
+
WATCHKIT_EXTENSION_KEY = 'WatchKit Extension'
|
45
|
+
WIDGET_KEY = 'Widget'
|
4
46
|
|
5
|
-
|
47
|
+
WATCHKIT_APP_PREFIX = WATCHKIT_APP_KEY + ' '
|
48
|
+
WATCHKIT_EXTENSION_PREFIX = WATCHKIT_EXTENSION_KEY + ' '
|
49
|
+
WIDGET_PREFIX = WIDGET_KEY + ' '
|
6
50
|
|
51
|
+
WATCHKIT_APP_SUFFIX = ' ' + WATCHKIT_APP_KEY
|
52
|
+
WATCHKIT_EXTENSION_SUFFIX = ' ' + WATCHKIT_EXTENSION_KEY
|
53
|
+
WIDGET_SUFFIX = ' ' + WIDGET_KEY
|
54
|
+
|
55
|
+
WATCHKIT_APP_SUFFIX_WITH_EXT = WATCHKIT_APP_SUFFIX + APP_EXT
|
56
|
+
WATCHKIT_EXTENSION_SUFFIX_WITH_EXT = WATCHKIT_EXTENSION_SUFFIX + APPEX_EXT
|
57
|
+
WIDGET_SUFFIX_WITH_EXT = WIDGET_SUFFIX + APPEX_EXT
|
58
|
+
|
59
|
+
IOS_BUILDS_FOLDER_NAME = 'build'
|
60
|
+
IOS_PLUGINS_FOLDER_NAME = 'PlugIns'
|
61
|
+
|
62
|
+
# unity
|
63
|
+
UNITY_BUILDS_IOS_PATH = 'Builds/iOS'
|
64
|
+
UNITY_BUILDS_IOS_PROJECT = 'Unity-iPhone.xcodeproj'
|
65
|
+
UNITY_BUILDS_IOS_SCHEME = 'Unity-iPhone'
|
66
|
+
UNITY_BUILDS_ICONS_PATH = 'Unity-iPhone/Images.xcassets'
|
67
|
+
UNITY_BUILD_SCRIPTS_PATH = 'UnityBuildAutomationScripts'
|
68
|
+
UNITY_ASSETS_EDITOR_PATH = 'Assets/Editor'
|
69
|
+
UNITY_BUILD_SCRIPTS_DIR = File.join(BUILD_SCRIPTS_PATH, UNITY_BUILD_SCRIPTS_PATH)
|
70
|
+
UNITY_EDITOR_DIR = File.join(Dir.pwd, UNITY_ASSETS_EDITOR_PATH)
|
71
|
+
UNITY_IOS_PROJECT_PATH = File.join(Dir.pwd, UNITY_BUILDS_IOS_PATH)
|
72
|
+
|
73
|
+
# project adjuster
|
7
74
|
ADJUSTER_WORKING_COPY_PATH = '/tmp/NIXProjectAdjuster'
|
8
75
|
ADJUSTER_TEMP_PROJECT_NAME = 'newProj'
|
9
76
|
|
10
|
-
|
77
|
+
# tests
|
78
|
+
TESTS_AND_COVERAGE_TIMEOUT = 10
|
11
79
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
80
|
+
# config keys
|
81
|
+
PROJECT_TO_BUILD_KEY = 'PROJECT_TO_BUILD'
|
82
|
+
PROJECT_TARGET_TO_BUILD_KEY = 'PROJECT_TARGET_TO_BUILD'
|
83
|
+
PROJECT_TARGET_TO_TEST_KEY = 'PROJECT_TARGET_TO_TEST'
|
84
|
+
WORKSPACE_TO_BUILD_KEY = 'WORKSPACE_TO_BUILD'
|
85
|
+
WORKSPACE_SCHEME_TO_BUILD_KEY = 'WORKSPACE_SCHEME_TO_BUILD'
|
86
|
+
WORKSPACE_SCHEME_TO_TEST_KEY = 'WORKSPACE_SCHEME_TO_TEST'
|
87
|
+
SDK_KEY = 'SDK'
|
88
|
+
SDK_FOR_TESTS_KEY = 'SDK_FOR_TESTS'
|
89
|
+
EXCLUDE_PATTERN_FOR_CODE_COVERAGE_KEY = 'EXCLUDE_PATTERN_FOR_CODE_COVERAGE'
|
90
|
+
EXCLUDE_PATTERN_FOR_CODE_DUPLICATION_KEY = 'EXCLUDE_PATTERN_FOR_CODE_DUPLICATION'
|
91
|
+
DEPLOY_HOST_KEY = 'DEPLOY_HOST'
|
92
|
+
DEPLOY_PATH_KEY = 'DEPLOY_PATH'
|
93
|
+
DEPLOY_USERNAME_KEY = 'DEPLOY_USERNAME'
|
94
|
+
DEPLOY_PASSWORD_KEY = 'DEPLOY_PASSWORD'
|
95
|
+
DEPLOY_ITUNESCONNECT_USERNAME_KEY = 'DEPLOY_ITUNESCONNECT_USERNAME'
|
96
|
+
ICONS_PATH_KEY = 'ICONS_PATH'
|
97
|
+
XCTEST_DESTINATION_DEVICE_KEY = 'XCTEST_DESTINATION_DEVICE'
|
98
|
+
CONFIGURATION_FILES_PATH_KEY = 'CONFIGURATION_FILES_PATH'
|
99
|
+
CODE_COVERAGE_CONFIGURATION_KEY = 'CODE_COVERAGE_CONFIGURATION'
|
100
|
+
CODE_COVERAGE_OUTPUT_DIRECTORY_KEY = 'CODE_COVERAGE_OUTPUT_DIRECTORY'
|
101
|
+
ENV_VAR_PREFIX_KEY = 'ENV_VAR_PREFIX'
|
102
|
+
BUNDLE_ID_KEY = 'BUNDLE_ID'
|
103
|
+
RESIGNED_BUNDLE_ID_KEY = 'RESIGNED_BUNDLE_ID'
|
104
|
+
RESIGNED_WATCHKIT_APP_BUNDLE_ID_KEY = 'RESIGNED_WATCHKIT_APP_BUNDLE_ID'
|
105
|
+
RESIGNED_WATCHKIT_EXTENSION_BUNDLE_ID_KEY = 'RESIGNED_WATCHKIT_EXTENSION_BUNDLE_ID'
|
106
|
+
RESIGNED_WIDGET_BUNDLE_ID_KEY = 'RESIGNED_WIDGET_BUNDLE_ID'
|
107
|
+
RESIGNED_BUNDLE_NAME_KEY = 'RESIGNED_BUNDLE_NAME'
|
108
|
+
RESIGNED_ENTITLEMENTS_PATH_KEY = 'RESIGNED_ENTITLEMENTS_PATH'
|
109
|
+
RESIGNED_WATCHKIT_EXTENSION_ENTITLEMENTS_PATH_KEY = 'RESIGNED_WATCHKIT_EXTENSION_ENTITLEMENTS_PATH'
|
110
|
+
RESIGNED_WIDGET_ENTITLEMENTS_PATH_KEY = 'RESIGNED_WIDGET_ENTITLEMENTS_PATH'
|
17
111
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
112
|
+
# env keys
|
113
|
+
PRODUCT_SETTINGS_PATH_KEY = 'PRODUCT_SETTINGS_PATH'
|
114
|
+
CONFIGURATION_BUILD_DIR_KEY = 'CONFIGURATION_BUILD_DIR'
|
115
|
+
DWARF_DSYM_FOLDER_PATH_KEY = 'DWARF_DSYM_FOLDER_PATH'
|
116
|
+
BUILT_PRODUCTS_DIR_KEY = 'BUILT_PRODUCTS_DIR'
|
117
|
+
EXECUTABLE_NAME_KEY = 'EXECUTABLE_NAME'
|
118
|
+
PROJECT_KEY = 'PROJECT'
|
119
|
+
OBJECT_FILE_DIR_NORMAL_KEY = 'OBJECT_FILE_DIR_normal'
|
120
|
+
EMBEDDED_PROFILE_NAME_KEY = 'EMBEDDED_PROFILE_NAME'
|
121
|
+
TARGET_NAME_KEY = 'TARGET_NAME'
|
122
|
+
CONFIGURATION_KEY = 'CONFIGURATION'
|
123
|
+
APP_PRODUCT_KEY = 'APP_PRODUCT'
|
124
|
+
SDK_NAME_KEY = 'SDK_NAME'
|
125
|
+
IPA_BUNDLE_ID_KEY = 'IPA_BUNDLE_ID'
|
126
|
+
CURRENT_APP_VERSION_KEY = 'CURRENT_APP_VERSION'
|
127
|
+
CURRENT_BUILD_VERSION_KEY = 'CURRENT_BUILD_VERSION'
|
128
|
+
NAME_FOR_DEPLOYMENT_KEY = 'NAME_FOR_DEPLOYMENT'
|
129
|
+
IPA_PRODUCT_KEY = 'IPA_PRODUCT'
|
130
|
+
APP_DSYM_KEY = 'APP_DSYM'
|
131
|
+
SCM_USERNAME_KEY = 'SCM_USERNAME'
|
132
|
+
SCM_PASSWORD_KEY = 'SCM_PASSWORD'
|
133
|
+
WATCHKIT_EXTENSION_PRODUCT_SETTINGS_PATH_KEY = 'WATCHKIT_EXTENSION_' + PRODUCT_SETTINGS_PATH_KEY
|
134
|
+
WATCHKIT_APP_PRODUCT_SETTINGS_PATH_KEY = 'WATCHKIT_APP_' + PRODUCT_SETTINGS_PATH_KEY
|
135
|
+
WIDGET_PRODUCT_SETTINGS_PATH_KEY = 'WIDGET_' + PRODUCT_SETTINGS_PATH_KEY
|
23
136
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# This is very basic git wrapper for internal Nixenvironment gem needs
|
2
|
+
# It's used instead of popular 'git' gem (https://rubygems.org/gems/git)
|
3
|
+
# because 'git' gem is redundant and also doesn't satisfy all Nixenvironment gem needs
|
4
|
+
|
5
|
+
require_relative 'cmd_executor'
|
6
|
+
|
7
|
+
module Nixenvironment
|
8
|
+
class Git < CmdExecutor
|
9
|
+
@binary_name = 'git'
|
10
|
+
|
11
|
+
HEAD = 'HEAD'
|
12
|
+
REMOTE_MASTER = 'refs/heads/master'
|
13
|
+
DEFAULT_REFSPEC = "#{HEAD}:#{REMOTE_MASTER}"
|
14
|
+
|
15
|
+
CLONE_CMD = 'clone'
|
16
|
+
FETCH_CMD = 'fetch'
|
17
|
+
CHECKOUT_CMD = 'checkout'
|
18
|
+
COMMIT_CMD = 'commit'
|
19
|
+
PUSH_CMD = 'push'
|
20
|
+
REMOTE_ADD_CMD = 'remote add'
|
21
|
+
STATUS_CMD = 'status'
|
22
|
+
TAG_CMD = 'tag'
|
23
|
+
|
24
|
+
RECURSIVE_OPT = '--recursive'
|
25
|
+
TAGS_OPT = '--tags'
|
26
|
+
ORPHAN_OPT = '--orphan'
|
27
|
+
MESSAGE_OPT = '--message'
|
28
|
+
FORCE_OPT = '--force'
|
29
|
+
PORCELAIN_OPT = '--porcelain'
|
30
|
+
|
31
|
+
# options
|
32
|
+
# :recursive, :r => after the clone is created, initialize all submodules within
|
33
|
+
def self.clone(url, working_dir = nil, options = {})
|
34
|
+
arr_opts = []
|
35
|
+
arr_opts << RECURSIVE_OPT if options[:recursive] || options[:r]
|
36
|
+
arr_opts << url
|
37
|
+
arr_opts << working_dir if working_dir.present?
|
38
|
+
|
39
|
+
execute(CLONE_CMD, arr_opts)
|
40
|
+
end
|
41
|
+
|
42
|
+
# options
|
43
|
+
# :tags, :t => fetch tags
|
44
|
+
def self.fetch(options = {})
|
45
|
+
arr_opts = []
|
46
|
+
arr_opts << TAGS_OPT if options[:tags] || options[:t]
|
47
|
+
|
48
|
+
execute(FETCH_CMD, arr_opts)
|
49
|
+
end
|
50
|
+
|
51
|
+
# options
|
52
|
+
# :orphan => orphan branch
|
53
|
+
def self.checkout(branch, options = {})
|
54
|
+
arr_opts = []
|
55
|
+
arr_opts << ORPHAN_OPT if options[:orphan]
|
56
|
+
arr_opts << branch
|
57
|
+
|
58
|
+
execute(CHECKOUT_CMD, arr_opts)
|
59
|
+
end
|
60
|
+
|
61
|
+
# options
|
62
|
+
# :message, :m => commit message
|
63
|
+
def self.commit(options = {})
|
64
|
+
message = options[:message] || options[:m]
|
65
|
+
arr_opts = []
|
66
|
+
arr_opts << "#{MESSAGE_OPT} '#{message}'" if message
|
67
|
+
|
68
|
+
execute(COMMIT_CMD, arr_opts)
|
69
|
+
end
|
70
|
+
|
71
|
+
# options
|
72
|
+
# :force, :f => force push, ignore checks
|
73
|
+
def self.push(remote = nil, refspec = nil, options = {})
|
74
|
+
arr_opts = []
|
75
|
+
arr_opts << FORCE_OPT if options[:force] || options[:f]
|
76
|
+
arr_opts << remote if remote.present?
|
77
|
+
arr_opts << refspec if refspec.present?
|
78
|
+
|
79
|
+
execute(PUSH_CMD, arr_opts)
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.remote_add(name, url)
|
83
|
+
arr_opts = []
|
84
|
+
arr_opts << name
|
85
|
+
arr_opts << url
|
86
|
+
|
87
|
+
execute(REMOTE_ADD_CMD, arr_opts)
|
88
|
+
end
|
89
|
+
|
90
|
+
# options
|
91
|
+
# :porcelain => give the output in an easy-to-parse format
|
92
|
+
def self.status(options = {})
|
93
|
+
arr_opts = []
|
94
|
+
arr_opts << PORCELAIN_OPT if options[:porcelain]
|
95
|
+
|
96
|
+
execute(STATUS_CMD, arr_opts)
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.tag
|
100
|
+
tags = execute(TAG_CMD).lines
|
101
|
+
tags.map!(&:strip!)
|
102
|
+
tags.sort_by!(&:to_i)
|
103
|
+
end
|
104
|
+
|
105
|
+
private_class_method :execute
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'cfpropertylist'
|
2
|
+
|
3
|
+
module Nixenvironment
|
4
|
+
class Plist
|
5
|
+
FORMAT_BINARY = CFPropertyList::List::FORMAT_BINARY
|
6
|
+
FORMAT_XML = CFPropertyList::List::FORMAT_XML
|
7
|
+
FORMAT_PLAIN = CFPropertyList::List::FORMAT_PLAIN
|
8
|
+
FORMAT_AUTO = CFPropertyList::List::FORMAT_AUTO
|
9
|
+
|
10
|
+
def self.from_file(path)
|
11
|
+
new(CFPropertyList::List.new(:file => path))
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.from_hash(hash)
|
15
|
+
plist = CFPropertyList::List.new
|
16
|
+
plist.value = CFPropertyList.guess(hash)
|
17
|
+
plist.format = FORMAT_XML
|
18
|
+
new(plist)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.from_str(str)
|
22
|
+
plist = CFPropertyList::List.new
|
23
|
+
plist.load_str(str)
|
24
|
+
new(plist)
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(plist)
|
28
|
+
@plist = plist
|
29
|
+
@path = @plist.filename
|
30
|
+
@data = CFPropertyList.native_types(@plist.value)
|
31
|
+
end
|
32
|
+
|
33
|
+
def [](key)
|
34
|
+
@data[key]
|
35
|
+
end
|
36
|
+
|
37
|
+
def []=(key, value)
|
38
|
+
@data[key] = value
|
39
|
+
@plist.value = CFPropertyList.guess(@data)
|
40
|
+
end
|
41
|
+
|
42
|
+
def save(path = nil, format = nil, formatted = true)
|
43
|
+
path ||= @path
|
44
|
+
raise 'Path to save plist is not specified!' unless path
|
45
|
+
@plist.save(path, format, :formatted => formatted)
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s(format = FORMAT_AUTO, formatted = true)
|
49
|
+
@plist.to_str(format, :formatted => formatted)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# This is very basic xcodebuild wrapper for internal Nixenvironment gem needs
|
2
|
+
# It's used instead of popular 'xcodeproj' gem (https://github.com/cocoapods/xcodeproj)
|
3
|
+
# because 'xcodeproj' gem is redundant and also doesn't satisfy all Nixenvironment gem needs
|
4
|
+
|
5
|
+
require_relative 'cmd_executor'
|
6
|
+
|
7
|
+
module Nixenvironment
|
8
|
+
class Xcodebuild < CmdExecutor
|
9
|
+
@binary_name = 'xcodebuild'
|
10
|
+
|
11
|
+
@config_settings = {}
|
12
|
+
@info = {}
|
13
|
+
|
14
|
+
def self.contains_config?(config)
|
15
|
+
configurations = @info['Build Configurations']
|
16
|
+
configurations && configurations.include?(config)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.contains_scheme?(scheme)
|
20
|
+
schemes = @info['Schemes']
|
21
|
+
schemes && schemes.include?(scheme)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.clean_all_targets
|
25
|
+
execute(nil, %w(-alltargets clean))
|
26
|
+
end
|
27
|
+
|
28
|
+
# other => other args to be passed to xcodebuild
|
29
|
+
def self.build(sdk = nil, config = nil, project = nil, target = nil, workspace = nil, scheme = nil, env_var_prefix = nil, other = {})
|
30
|
+
build_args = []
|
31
|
+
build_args << "-sdk '#{sdk}'" if sdk.present?
|
32
|
+
build_args << "-configuration '#{config}'" if config.present?
|
33
|
+
build_args << "-project '#{project}'" if project.present?
|
34
|
+
build_args << "-target '#{target}'" if target.present?
|
35
|
+
build_args << "-workspace '#{workspace}'" if workspace.present?
|
36
|
+
build_args << "-scheme '#{scheme}'" if scheme.present?
|
37
|
+
|
38
|
+
if env_var_prefix.present?
|
39
|
+
vars_to_define = ''
|
40
|
+
|
41
|
+
ENV.each do |key, value|
|
42
|
+
if key.start_with?(env_var_prefix)
|
43
|
+
vars_to_define += ' ' + key
|
44
|
+
puts "Environment variable '#{key}' will be defined with value: #{value}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
build_args << "GCC_PREPROCESSOR_DEFINITIONS='$GCC_PREPROCESSOR_DEFINITIONS#{vars_to_define}'" if vars_to_define.present?
|
49
|
+
end
|
50
|
+
|
51
|
+
# set "Precompile Prefix Header" flag to "NO";
|
52
|
+
# otherwise it can throw error ".pch.pch has been modified since the precompiled header"
|
53
|
+
build_args << 'GCC_PRECOMPILE_PREFIX_HEADER=NO'
|
54
|
+
build_args << other.map { |key, value| "#{key}='#{value}'" }.join(' ')
|
55
|
+
build_args << '| xcpretty -c; exit ${PIPESTATUS[0]}'
|
56
|
+
|
57
|
+
execute(nil, build_args)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.test(sdk = nil, config = nil, workspace = nil, scheme = nil, code_coverage_config = nil)
|
61
|
+
build_args = []
|
62
|
+
build_args << "-sdk '#{sdk}'" if sdk.present?
|
63
|
+
build_args << "-configuration '#{config}'" if config.present?
|
64
|
+
build_args << "-workspace '#{workspace}'" if workspace.present?
|
65
|
+
build_args << "-scheme '#{scheme}'" if scheme.present?
|
66
|
+
build_args << "-xcconfig '#{code_coverage_config}'" if code_coverage_config.present?
|
67
|
+
build_args << "-destination-timeout '#{timeout}'" if timeout.present?
|
68
|
+
build_args << "-destination '#{destination}'" if destination.present?
|
69
|
+
build_args << " | ocunit2junit"
|
70
|
+
|
71
|
+
execute('test', build_args)
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.read_config_settings(workspace = nil, project = nil, scheme = nil, target = nil, sdk = nil, config = nil)
|
75
|
+
read_info
|
76
|
+
|
77
|
+
raise "Scheme #{scheme} doesn't exist!" if scheme.present? && !contains_scheme?(scheme)
|
78
|
+
raise "Configuration #{config} doesn't exist!" if config.present? && !contains_config?(config)
|
79
|
+
raise "Either #{PROJECT_TO_BUILD_KEY} or #{WORKSPACE_TO_BUILD_KEY} must be specified!" if project.blank? && workspace.blank?
|
80
|
+
|
81
|
+
build_args = []
|
82
|
+
build_args << "-sdk '#{sdk}'" if sdk.present?
|
83
|
+
build_args << "-configuration '#{config}'" if config.present?
|
84
|
+
build_args << "-project '#{project}'" if project.present?
|
85
|
+
build_args << "-target '#{target}'" if target.present?
|
86
|
+
build_args << "-workspace '#{workspace}'" if workspace.present?
|
87
|
+
build_args << "-scheme '#{scheme}'" if scheme.present?
|
88
|
+
build_args << '-showBuildSettings'
|
89
|
+
|
90
|
+
puts 'Reading config settings ...'
|
91
|
+
cmd_output = execute(nil, build_args, true)
|
92
|
+
|
93
|
+
env_vars_list = cmd_output.split(/\n/).reject(&:empty?)
|
94
|
+
if env_vars_list.present?
|
95
|
+
build_settings_to_strip = Hash[env_vars_list.map { |it| it.split('=', 2) }]
|
96
|
+
build_settings_to_strip.each do |key, value|
|
97
|
+
@config_settings[key.strip] ||= value.strip if key.present? && value.present?
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
if workspace.present?
|
102
|
+
@config_settings[WATCHKIT_EXTENSION_PRODUCT_SETTINGS_PATH_KEY] = product_settings_path(workspace, scheme, WATCHKIT_EXTENSION_SUFFIX)
|
103
|
+
@config_settings[WATCHKIT_APP_PRODUCT_SETTINGS_PATH_KEY] = product_settings_path(workspace, scheme, WATCHKIT_APP_SUFFIX)
|
104
|
+
@config_settings[WIDGET_PRODUCT_SETTINGS_PATH_KEY] = product_settings_path(workspace, scheme, WIDGET_SUFFIX)
|
105
|
+
end
|
106
|
+
|
107
|
+
build_directory = File.join(Dir.pwd, IOS_BUILDS_FOLDER_NAME)
|
108
|
+
@config_settings[CONFIGURATION_BUILD_DIR_KEY] = build_directory
|
109
|
+
@config_settings[BUILT_PRODUCTS_DIR_KEY] = build_directory
|
110
|
+
@config_settings[DWARF_DSYM_FOLDER_PATH_KEY] = build_directory
|
111
|
+
|
112
|
+
plist_path = @config_settings[PRODUCT_SETTINGS_PATH_KEY]
|
113
|
+
info_plist = Plist.from_file(plist_path)
|
114
|
+
@config_settings[RESIGNED_BUNDLE_NAME_KEY] ||= info_plist['CFBundleDisplayName']
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.product_settings_path(workspace, scheme, scheme_suffix)
|
118
|
+
scheme += scheme_suffix
|
119
|
+
if contains_scheme?(scheme)
|
120
|
+
build_args = []
|
121
|
+
build_args << "-workspace '#{workspace}'"
|
122
|
+
build_args << "-scheme '#{scheme}'"
|
123
|
+
build_args << '-showBuildSettings'
|
124
|
+
|
125
|
+
puts "Getting product settings path for scheme '#{scheme}' ..."
|
126
|
+
cmd_output = execute(nil, build_args, true)
|
127
|
+
|
128
|
+
env_vars_list = cmd_output.split(/\n/).reject(&:empty?)
|
129
|
+
if env_vars_list.present?
|
130
|
+
build_settings_to_strip = Hash.new.compare_by_identity
|
131
|
+
env_vars_list.each do |it|
|
132
|
+
key_value = it.split('=', 2)
|
133
|
+
build_settings_to_strip[key_value.first] = key_value.last
|
134
|
+
end
|
135
|
+
|
136
|
+
build_settings_to_strip.each do |key, value|
|
137
|
+
if key.present? && value.present?
|
138
|
+
stripped_key = key.strip
|
139
|
+
stripped_value = value.strip
|
140
|
+
return stripped_value if stripped_key == PRODUCT_SETTINGS_PATH_KEY && stripped_value.include?(scheme_suffix)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
nil
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.read_info
|
150
|
+
cmd_output = execute(nil, %w(-list 2>&1))
|
151
|
+
raise "Getting xcode_project_info error! #{cmd_output}" if cmd_output.include?('error')
|
152
|
+
|
153
|
+
cmd_output = cmd_output.lines[1..-1].join.split(/\n\n/)
|
154
|
+
cmd_output.each do |pair|
|
155
|
+
key, value = pair.split(/:/)
|
156
|
+
next unless key.present? && value.present?
|
157
|
+
@info[key.strip] = value.lines.map(&:strip).reject(&:empty?)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.config_settings
|
162
|
+
@config_settings
|
163
|
+
end
|
164
|
+
|
165
|
+
private_class_method :product_settings_path, :read_info, :execute
|
166
|
+
end
|
167
|
+
end
|