nixenvironment 0.0.59 → 0.0.60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/README.md +14 -14
  4. data/bin/Config +2 -2
  5. data/bin/nixenvironment +420 -842
  6. data/legacy/CleanWorkingCopy.sh +69 -0
  7. data/legacy/Deploy.sh +44 -0
  8. data/legacy/DeployAPK.py +58 -0
  9. data/legacy/DeployIPA.sh +125 -0
  10. data/legacy/DetectSCM.sh +11 -0
  11. data/legacy/GenerateCodeCoverageForXCTests.sh +134 -0
  12. data/legacy/GenerateCodeDuplicationReport.sh +24 -0
  13. data/legacy/IncrementBuildNumber.py +129 -0
  14. data/legacy/LoadBuildEnvVars.sh +116 -0
  15. data/legacy/MakeTag.sh +94 -0
  16. data/legacy/RemoveTemporaryFiles.sh +9 -0
  17. data/legacy/SaveRevision.sh +122 -0
  18. data/legacy/UnityBuildAndroid.py +84 -0
  19. data/legacy/UnityBuildAutomationScripts/CommandLineReader.cs +130 -0
  20. data/legacy/UnityBuildAutomationScripts/NIXBuilder.cs +105 -0
  21. data/legacy/UnityBuildEnvVars.py +41 -0
  22. data/legacy/VerifyBinarySigning.py +80 -0
  23. data/legacy/svn-clean.pl +246 -0
  24. data/legacy/svncopy.pl +1134 -0
  25. data/lib/nixenvironment.rb +5 -0
  26. data/lib/nixenvironment/archiver.rb +690 -0
  27. data/lib/nixenvironment/build_env_vars_loader.rb +24 -0
  28. data/lib/nixenvironment/cmd_executor.rb +33 -0
  29. data/lib/nixenvironment/config.rb +127 -14
  30. data/lib/nixenvironment/git.rb +107 -0
  31. data/lib/nixenvironment/plist.rb +52 -0
  32. data/lib/nixenvironment/version.rb +1 -1
  33. data/lib/nixenvironment/xcodebuild.rb +167 -0
  34. data/nixenvironment.gemspec +6 -0
  35. data/utils/XcodeIconTagger/IconTagger +0 -0
  36. data/utils/XcodeIconTagger/masks/OneLineMask.png +0 -0
  37. data/utils/XcodeIconTagger/masks/TwoLineMask.png +0 -0
  38. data/utils/aapt +0 -0
  39. data/utils/gcovr +986 -0
  40. data/utils/identitieslist +0 -0
  41. data/utils/simian-2.3.33.jar +0 -0
  42. 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
- NIXENV_ROOT = '.nixenvironment'
3
- BUILD_SCRIPTS = 'ninbas'
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
- BUILD_SCRIPTS_PATH = File.join(Dir.home, NIXENV_ROOT, BUILD_SCRIPTS)
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
- UNITY_BUILDS_IOS_PATH = 'Builds/iOS'
77
+ # tests
78
+ TESTS_AND_COVERAGE_TIMEOUT = 10
11
79
 
12
- REPO_LIST = {
13
- BUILD_SCRIPTS => 'https://bitbucket.org/nixsolutions/ninbas.git',
14
- # 'nixipl' => 'https://bitbucket.org/nixsolutions/nixipl.git',
15
- # 'nocl' => 'https://bitbucket.org/nixsolutions/nocl.git'
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
- TEMPLATES_REPO_LIST = {
19
- 'objc' => 'https://bitbucket.org/nixsolutions/np.git',
20
- 'swift' => 'https://bitbucket.org/nixsolutions/nps.git',
21
- 'unity' => 'https://bitbucket.org/nixsolutions/nup.git'
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
@@ -1,3 +1,3 @@
1
1
  module Nixenvironment
2
- VERSION = '0.0.59'
2
+ VERSION = '0.0.60'
3
3
  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