diff --git a/scripts/IosSetupSteps.txt b/scripts/IosSetupSteps.txt index 58c1abf..262d6c6 100644 --- a/scripts/IosSetupSteps.txt +++ b/scripts/IosSetupSteps.txt @@ -1,3 +1,5 @@ +sh echo 'IosSetupSteps.txt' + # restore from backup # восстанавливаем из бэкапа (исходники от сборки предыдущей конфигурации могут быть модифицированными) # create backup @@ -6,7 +8,7 @@ sh echo '{@sln_config}' inside 'BuildSample/BuildSample.sln' remove NotCompileApp project inside 'BuildSample/BuildSample/CoolApp.csproj' set OutputPath to 'Output' for '{@sln_config}' -inside 'BuildSample/BuildSample/CoolApp.csproj' set AssemblyName to 'CoolApp' for '' +inside 'BuildSample/BuildSample/CoolApp.csproj' set AssemblyName to '{@assembly_name}' for '' inside 'BuildSample/BuildSample/Info.plist' set CFBundleVersion to '{@version}' inside 'BuildSample/BuildSample/Info.plist' set CFBundleDisplayName to 'CoolApp' diff --git a/scripts/IosSteps.txt b/scripts/IosSteps.txt index 16383c3..e748de9 100644 --- a/scripts/IosSteps.txt +++ b/scripts/IosSteps.txt @@ -1,3 +1,4 @@ +sh echo 'IosSteps.txt' sh echo '{@builder_path}' @@ -9,10 +10,10 @@ clean 'BuildSample/BuildSample.sln' for '{@sln_config}' build 'BuildSample/BuildSample.sln' for '{@sln_config}' create dirs 'Output/Appstore/Artifacts' -copy 'BuildSample/BuildSample/Output/BuildSample-{@version}.ipa' to 'Output/Appstore/Artifacts' +copy 'BuildSample/BuildSample/Output/{@assembly_name}-{@version}.ipa' to 'Output/Appstore/Artifacts' sh cp -a BuildSample/BuildSample/Output/ Output/Appstore/ -publish 'Output/Appstore/Artifacts/BuildSample-{@version}.ipa' to testflight notes = 'Hello' api_token = '{@tf_api_token}' team_token = '{@tf_team_token}' +publish 'Output/Appstore/Artifacts/{@assembly_name}-{@version}.ipa' to testflight notes = 'Hello' api_token = '{@tf_api_token}' team_token = '{@tf_team_token}' -restore from backup -delete backup \ No newline at end of file +#restore from backup +#delete backup \ No newline at end of file diff --git a/scripts/TouchinBuild/Core/LineConveyor/CommentRemover.py b/scripts/TouchinBuild/Core/LineConveyor/CommentRemover.py index 7dcb8c1..0628456 100644 --- a/scripts/TouchinBuild/Core/LineConveyor/CommentRemover.py +++ b/scripts/TouchinBuild/Core/LineConveyor/CommentRemover.py @@ -1,6 +1,9 @@ -class CommentRemover: +from Core.LineConveyor.PreprocessorBase import PreprocessorBase + + +class CommentRemover(PreprocessorBase): def __init__(self): - pass + PreprocessorBase.__init__(self) def processText(self, line, conveyorProcessor): assert line is not None diff --git a/scripts/TouchinBuild/Core/LineConveyor/MacroResolver.py b/scripts/TouchinBuild/Core/LineConveyor/MacroResolver.py index 0012c09..e42dba2 100644 --- a/scripts/TouchinBuild/Core/LineConveyor/MacroResolver.py +++ b/scripts/TouchinBuild/Core/LineConveyor/MacroResolver.py @@ -1,5 +1,10 @@ -class MacroResolver: +from Core.LineConveyor.PreprocessorBase import PreprocessorBase + + +class MacroResolver(PreprocessorBase): def __init__(self, macroProcessor, valueProvider): + PreprocessorBase.__init__(self) + assert macroProcessor is not None assert valueProvider is not None diff --git a/scripts/TouchinBuild/Core/LineConveyor/NullPreprocessor.py b/scripts/TouchinBuild/Core/LineConveyor/NullPreprocessor.py new file mode 100644 index 0000000..67824da --- /dev/null +++ b/scripts/TouchinBuild/Core/LineConveyor/NullPreprocessor.py @@ -0,0 +1,9 @@ +from Core.LineConveyor.PreprocessorBase import PreprocessorBase + + +class NullPreprocessor(PreprocessorBase): + def __init__(self): + PreprocessorBase.__init__(self) + + def processText(self, text, conveyorProcessor): + return text diff --git a/scripts/TouchinBuild/Core/LineConveyor/PreprocessorBase.py b/scripts/TouchinBuild/Core/LineConveyor/PreprocessorBase.py new file mode 100644 index 0000000..2a18eda --- /dev/null +++ b/scripts/TouchinBuild/Core/LineConveyor/PreprocessorBase.py @@ -0,0 +1,12 @@ +import abc + + +class PreprocessorBase: + __metaclass__ = abc.ABCMeta + + def __init__(self): + pass + + @abc.abstractmethod + def processText(self, line, conveyorProcessor): + pass \ No newline at end of file diff --git a/scripts/TouchinBuild/Core/LineConveyor/Stripper.py b/scripts/TouchinBuild/Core/LineConveyor/Stripper.py index 8aff211..4c21c3a 100644 --- a/scripts/TouchinBuild/Core/LineConveyor/Stripper.py +++ b/scripts/TouchinBuild/Core/LineConveyor/Stripper.py @@ -1,6 +1,9 @@ -class Stripper: +from Core.LineConveyor.PreprocessorBase import PreprocessorBase + + +class Stripper(PreprocessorBase): def __init__(self): - pass + PreprocessorBase.__init__(self) def processText(self, line, conveyorProcessor): assert line is not None diff --git a/scripts/TouchinBuild/Core/LineConveyor/TextConveyorPreprocessor.py b/scripts/TouchinBuild/Core/LineConveyor/TextConveyorPreprocessor.py index 9f789b4..cf43e00 100644 --- a/scripts/TouchinBuild/Core/LineConveyor/TextConveyorPreprocessor.py +++ b/scripts/TouchinBuild/Core/LineConveyor/TextConveyorPreprocessor.py @@ -1,5 +1,9 @@ -class TextConveyorPreprocessor: +from Core.LineConveyor.PreprocessorBase import PreprocessorBase + + +class TextConveyorPreprocessor(PreprocessorBase): def __init__(self): + PreprocessorBase.__init__(self) self.processors = [] def addProcessor(self, processor): diff --git a/scripts/TouchinBuild/Core/LineConveyor/TextInclude.py b/scripts/TouchinBuild/Core/LineConveyor/TextInclude.py index 144eea8..6c272e2 100644 --- a/scripts/TouchinBuild/Core/LineConveyor/TextInclude.py +++ b/scripts/TouchinBuild/Core/LineConveyor/TextInclude.py @@ -1,5 +1,10 @@ -class TextInclude: +from Core.LineConveyor.PreprocessorBase import PreprocessorBase + + +class TextInclude(PreprocessorBase): def __init__(self, includeProcessor, contentProvider): + PreprocessorBase.__init__(self) + assert includeProcessor is not None assert contentProvider is not None diff --git a/scripts/TouchinBuild/Tests/UnitTests/LineConveyor/test_commentRemover.py b/scripts/TouchinBuild/Tests/UnitTests/LineConveyor/test_commentRemover.py index 028d416..8cff836 100644 --- a/scripts/TouchinBuild/Tests/UnitTests/LineConveyor/test_commentRemover.py +++ b/scripts/TouchinBuild/Tests/UnitTests/LineConveyor/test_commentRemover.py @@ -8,12 +8,12 @@ class TestCommentRemover(unittest.TestCase): def test_startsWithComment(self): line = '# this line is comment' - newLine = self.commentRemover.processText(line, self.commentRemover) + newLine = self.commentRemover.processText(line, None) self.assertEqual('', newLine) def test_containsComment(self): line = 'this line contains # a comment' - newLine = self.commentRemover.processText(line, self.commentRemover) + newLine = self.commentRemover.processText(line, None) self.assertEqual('this line contains ', newLine) \ No newline at end of file diff --git a/scripts/TouchinBuild/Tests/UnitTests/SettingsMerger/__init__.py b/scripts/TouchinBuild/Tests/UnitTests/SettingsMerger/__init__.py new file mode 100644 index 0000000..cc31abc --- /dev/null +++ b/scripts/TouchinBuild/Tests/UnitTests/SettingsMerger/__init__.py @@ -0,0 +1 @@ +__author__ = 'rzaitov' diff --git a/scripts/TouchinBuild/Tests/UnitTests/SettingsMerger/test_SettingsMerger.py b/scripts/TouchinBuild/Tests/UnitTests/SettingsMerger/test_SettingsMerger.py new file mode 100644 index 0000000..88f53fc --- /dev/null +++ b/scripts/TouchinBuild/Tests/UnitTests/SettingsMerger/test_SettingsMerger.py @@ -0,0 +1,111 @@ +import unittest +from parsers.SettingsParser.SettingsMerger import SettingsMerger + + +class TestSettingsMerger(unittest.TestCase): + def setUp(self): + self.merger = SettingsMerger() + + self.child1 = { + 'sub_key1': 'value3', + 'sub_key2': 'value4', + } + + self.child2 = { + 'sub_key3': 'value5', + 'sub_key4': 'value6', + } + + self.globalSettings = { + 'top_level_key1': 'value1', + 'top_level_key2': 'value2', + + 'child1': self.child1, + 'child2': self.child2 + } + + def test_mergeTopLevelSettings(self): + description = { + 'segments': ['top_level_key1'], + 'value': 'new_value1' + } + + self.merger.merge(self.globalSettings, description) + + self.assertEqual('new_value1', self.globalSettings['top_level_key1']) + self.assertEqual('value2', self.globalSettings['top_level_key2']) + + def test_mergeSubElement(self): + description = { + 'segments': ['child1', 'sub_key1'], + 'value': 'new_value3' + } + + self.merger.merge(self.globalSettings, description) + + self.assertEqual('value1', self.globalSettings['top_level_key1']) + self.assertEqual('value2', self.globalSettings['top_level_key2']) + + self.assertEqual('new_value3', self.globalSettings['child1']['sub_key1']) + self.assertEqual('value4', self.globalSettings['child1']['sub_key2']) + + def test_getPropertyName(self): + self.checkName(['one', 'two', 'three'], 'three') + self.checkName(['one', 'two'], 'two') + self.checkName(['one'], 'one') + + def checkName(self, segments, expectedName): + name = self.merger.getPropertyName(segments) + self.assertEqual(name, expectedName) + + def test_checkPath(self): + self.checkPath(['one', 'two', 'three'], ['one', 'two']) + self.checkPath(['one', 'two'], ['one']) + self.checkPath(['one'], []) + + def checkPath(self, segments, expectedPath): + path = self.merger.getPath(segments) + + self.assertListEqual(expectedPath, path) + + def test_mergeNotExistTopLevelSetting(self): + description = { + 'segments': ['new_key'], + 'value': 'new_value' + } + + self.merger.merge(self.globalSettings, description) + self.assertEqual('new_value', self.globalSettings['new_key']) + + def test_mergeNotExistSubSetting(self): + description = { + 'segments': ['child1', 'new_key'], + 'value': 'new_value' + } + + self.merger.merge(self.globalSettings, description) + self.assertEqual('new_value', self.globalSettings['child1']['new_key']) + + + def test_mergeNotExistSub(self): + description = { + 'segments': ['child3', 'new_key'], + 'value': 'new_value' + } + + self.merger.merge(self.globalSettings, description) + self.assertEqual('new_value', self.globalSettings['child3']['new_key']) + + + def test_getSettingsDictionaryByPath(self): + dictionary1 = self.merger.getSettingsDictByPath(self.globalSettings, []) + self.assertEqual(self.globalSettings, dictionary1) + self.assertTrue(self.globalSettings == dictionary1) + + dictionary2 = self.merger.getSettingsDictByPath(self.globalSettings, ['child1']) + self.assertTrue(self.child1 == dictionary2) + + dictionary3 = self.merger.getSettingsDictByPath(self.globalSettings, ['child2']) + self.assertTrue(self.child2 == dictionary3) + + diff --git a/scripts/TouchinBuild/Tests/UnitTests/SettingsParser/test_SettingsParser.py b/scripts/TouchinBuild/Tests/UnitTests/SettingsParser/test_SettingsParser.py index c8081f5..6888f29 100644 --- a/scripts/TouchinBuild/Tests/UnitTests/SettingsParser/test_SettingsParser.py +++ b/scripts/TouchinBuild/Tests/UnitTests/SettingsParser/test_SettingsParser.py @@ -1,11 +1,14 @@ +# -*- coding: utf-8 -*- import unittest +from Core.LineConveyor.NullPreprocessor import NullPreprocessor from parsers.SettingsParser.SettingsParser import SettingsParser class TestSettingsParser(unittest.TestCase): def setUp(self): - self.parser = SettingsParser() + self.preprocessor = NullPreprocessor() + self.parser = SettingsParser(self.preprocessor) def test_processLine(self): line1 = "x.y.name1 = 'value1'" @@ -38,15 +41,16 @@ class TestSettingsParser(unittest.TestCase): self.assertEqual('value8', self.parser.settings['a']['z']['name2']) def test_emptyLinesAndComments(self): + class PartialSettingsParser(SettingsParser): - def __init__(self): - SettingsParser.__init__(self) + def __init__(self, textPreprocessor): + SettingsParser.__init__(self, textPreprocessor) self.processLineCall = 0 def processLine(self, line): self.processLineCall += 1 - self.parser = PartialSettingsParser() + self.parser = PartialSettingsParser(self.preprocessor) content = """ valid.line.with.setting = 'some value' # this is comment @@ -56,7 +60,9 @@ valid.line.with.setting = 'some value' self.parser.parse(content) - self.assertEqual(2, self.parser.processLineCall) + # всего 6 строк, 2 из которых пустые + # NullPreprocessor не уберет комментарии, поэтому будет 4 вызова processLine + self.assertEqual(4, self.parser.processLineCall) diff --git a/scripts/TouchinBuild/parsers/SettingsParser/SettingsMerger.py b/scripts/TouchinBuild/parsers/SettingsParser/SettingsMerger.py index 73af7dc..b45b829 100644 --- a/scripts/TouchinBuild/parsers/SettingsParser/SettingsMerger.py +++ b/scripts/TouchinBuild/parsers/SettingsParser/SettingsMerger.py @@ -6,12 +6,10 @@ class SettingsMerger: value = settingDescription['value'] segments = settingDescription['segments'] - propPath = segments[0:-1] - propName = segments[-1] + propPath = self.getPath(segments) + propName = self.getPropertyName(segments) settingsDict = self.getSettingsDictByPath(globalSettings, propPath) - #self.overrideGuard(settingsDict, propName, propPath) - settingsDict[propName] = value def getSettingsDictByPath(self, globalSettings, pathToSettingsDict): @@ -26,10 +24,10 @@ class SettingsMerger: return settingsDict - #def overrideGuard(self, dictionary, key, path): - # if key in dictionary: - # pathStr = '.'.join(path) - # msg = 'settings with name {0} by path {1} already exists with value {3}'.format(key, dictionary[key], pathStr) - # raise Exception(msg) - + def getPath(self, segments): + assert segments is not None + return segments[0:-1] + def getPropertyName(self, segments): + assert segments is not None + return segments[-1] \ No newline at end of file diff --git a/scripts/TouchinBuild/parsers/SettingsParser/SettingsParser.py b/scripts/TouchinBuild/parsers/SettingsParser/SettingsParser.py index 268d8d3..81cff59 100644 --- a/scripts/TouchinBuild/parsers/SettingsParser/SettingsParser.py +++ b/scripts/TouchinBuild/parsers/SettingsParser/SettingsParser.py @@ -3,10 +3,14 @@ from parsers.SettingsParser.SettingsMerger import SettingsMerger class SettingsParser: - def __init__(self, settings=None): - self.settings = settings + def __init__(self, compositeLineProcessor, settings): + assert compositeLineProcessor is not None + assert settings is None or type(settings) == dict - if not self.settings: + self.compositeLineProcessor = compositeLineProcessor + + self.settings = settings + if self.settings is None: self.settings = {} def parse(self, content): @@ -14,14 +18,12 @@ class SettingsParser: lines = content.splitlines() for line in lines: - stripped = line.strip(' \t\n\r') + processedLine = self.compositeLineProcessor.processText(line, None) - if len(stripped) == 0: + if len(processedLine) == 0: continue - if stripped.startswith("#"): - continue - - self.processLine(stripped) + else: + self.processLine(processedLine) def processLine(self, line): assert line is not None diff --git a/scripts/TouchinBuild/taskRunner.py b/scripts/TouchinBuild/taskRunner.py index 0985a88..d1f49cb 100755 --- a/scripts/TouchinBuild/taskRunner.py +++ b/scripts/TouchinBuild/taskRunner.py @@ -26,17 +26,17 @@ scriptDir = os.path.dirname(scriptFilePath) class TaskRunner: - def __init__(self, settingsProvider, fileContentProvider, buildConfigProvider): + def __init__(self, settingsProvider, fileContentProvider, buildConfigProvider, linePreprocessor): assert settingsProvider is not None assert fileContentProvider is not None assert buildConfigProvider is not None + assert linePreprocessor is not None self.settingsProvider = settingsProvider self.fileContentProvider = fileContentProvider self.configsProvider = buildConfigProvider + self.linePreprocessor = linePreprocessor - lineStripper = Stripper() - commentRemover = CommentRemover() macroProcessor = MacroProcessor() self.valueProvider = ValueProvider() @@ -52,9 +52,6 @@ class TaskRunner: self.textPreprocessor.addProcessor(macroResolver) self.textPreprocessor.addProcessor(textInclude) - self.linePreprocessor = TextConveyorPreprocessor() - self.linePreprocessor.addProcessor(commentRemover) - self.linePreprocessor.addProcessor(lineStripper) def run(self): rawSettings = self.settingsProvider.fetchSettings() @@ -83,9 +80,17 @@ if __name__ == "__main__": parser = argparse.ArgumentParser() overrideArgs = parser.parse_known_args()[1] + # компоную препроцессор для индивидуальной обработки строк (удаление комментариев и ведущих пробельных символов) + lineStripper = Stripper() + commentRemover = CommentRemover() + linePreprocessor = TextConveyorPreprocessor() + linePreprocessor.addProcessor(commentRemover) + linePreprocessor.addProcessor(lineStripper) + # TODO: перенести в корень комапановки - fromFileSettingsProvider = FromFileSettingsProvider() - overrideWithCmdSetProvider = CmdArgsOverriderSettingsProvider(fromFileSettingsProvider, overrideArgs) + settingsPath = 'scripts/settings.txt' + fromFileSettingsProvider = FromFileSettingsProvider(settingsPath, linePreprocessor) + overrideWithCmdSetProvider = CmdArgsOverriderSettingsProvider(fromFileSettingsProvider, overrideArgs, linePreprocessor) fContentProvider = FileContentProvider() @@ -96,5 +101,5 @@ if __name__ == "__main__": resolvedBuildConfigProvider = ResolvedBuildConfigProvider(predefineBuildConfigProvider) - runner = TaskRunner(overrideWithCmdSetProvider, fContentProvider, resolvedBuildConfigProvider) + runner = TaskRunner(overrideWithCmdSetProvider, fContentProvider, resolvedBuildConfigProvider, linePreprocessor) runner.run() \ No newline at end of file diff --git a/scripts/TouchinBuild/utils/SettingsProvider/CmdArgsOverriderSettingsProvider.py b/scripts/TouchinBuild/utils/SettingsProvider/CmdArgsOverriderSettingsProvider.py index 47ecb6c..2c4572b 100644 --- a/scripts/TouchinBuild/utils/SettingsProvider/CmdArgsOverriderSettingsProvider.py +++ b/scripts/TouchinBuild/utils/SettingsProvider/CmdArgsOverriderSettingsProvider.py @@ -3,12 +3,14 @@ from parsers.SettingsParser.SettingsParser import SettingsParser class CmdArgsOverriderSettingsProvider(SettingsProviderBase): - def __init__(self, settingsProvider, settingLines): + def __init__(self, settingsProvider, settingLines, compositeLineProcessor): SettingsProviderBase.__init__(self) assert settingsProvider is not None + assert compositeLineProcessor is not None self.inner = settingsProvider self.overrideSettings = settingLines + self.compositeLineProcessor = compositeLineProcessor def fetchSettings(self): settings = self.inner.fetchSettings() @@ -16,7 +18,7 @@ class CmdArgsOverriderSettingsProvider(SettingsProviderBase): if self.overrideSettings: for s in self.overrideSettings: line = self.normalizeLine(s) - settingParser = SettingsParser(settings) + settingParser = SettingsParser(self.compositeLineProcessor, settings) settingParser.processLine(line) return settings diff --git a/scripts/TouchinBuild/utils/SettingsProvider/FromFileSettingsProvider.py b/scripts/TouchinBuild/utils/SettingsProvider/FromFileSettingsProvider.py index 6e9e614..6df08db 100644 --- a/scripts/TouchinBuild/utils/SettingsProvider/FromFileSettingsProvider.py +++ b/scripts/TouchinBuild/utils/SettingsProvider/FromFileSettingsProvider.py @@ -3,14 +3,20 @@ from parsers.SettingsParser.SettingsParser import SettingsParser class FromFileSettingsProvider(SettingsProviderBase): - def __init__(self): + def __init__(self, pathToSettings, compositeLineProcessor): SettingsProviderBase.__init__(self) + assert pathToSettings is not None + assert compositeLineProcessor is not None + + self.pathToSettings = pathToSettings + self.compositeLineProcessor = compositeLineProcessor + def fetchSettings(self): - settingsFile = open('scripts/settings.txt') + settingsFile = open(self.pathToSettings) content = settingsFile.read() - parser = SettingsParser() + parser = SettingsParser(self.compositeLineProcessor, None) parser.parse(content) return parser.settings diff --git a/scripts/settings.txt b/scripts/settings.txt index 17963b1..277d2b8 100644 --- a/scripts/settings.txt +++ b/scripts/settings.txt @@ -1,12 +1,13 @@ # global settings build_tool = '/Applications/Xamarin\ Studio.app/Contents/MacOS/mdtool' -version = '0.0.0' +version = '0.0.0' # комментарий в тойже строке configs = 'appstore, staging' # ios platform settings ios.sln_config = 'Release|iPhone' ios.steps = 'scripts/IosSteps.txt' ios.setup_steps = 'IosSetupSteps.txt' +ios.assembly_name = 'CoolApp' ios.tf_api_token = '0e6925075d4fc10fed0e7bbf43fa6894_NjQ0OTI2MjAxMi0wOS0yNSAxMTo0MDozNi40OTY5MjU' ios.tf_team_token = 'c5c3cf7a6dae2bea4382dfbd181a2075_Mjc4ODkwMjAxMy0wOS0yOSAxNDowOTo1OC40Mzg5MTY'