Added buildbot script for automatic build and check sample apps. 72/61172/14
authorTomasz Olszak <t.olszak@samsung.com>
Fri, 4 Mar 2016 09:32:03 +0000 (10:32 +0100)
committerTomasz Olszak <t.olszak@samsung.com>
Thu, 10 Mar 2016 07:48:23 +0000 (08:48 +0100)
Change-Id: I51cc07631e1a398b50aecc94cabad9130ba342b9

tool/development/buildbot/.gitignore [new file with mode: 0644]
tool/development/buildbot/README [new file with mode: 0644]
tool/development/buildbot/crontab.template [new file with mode: 0644]
tool/development/buildbot/native-samples.cfg.template [new file with mode: 0644]
tool/development/buildbot/nativesamples.py [new file with mode: 0644]
tool/development/buildbot/poll_and_build_changes.py [new file with mode: 0755]
tool/development/buildbot/test_configuration.py [new file with mode: 0755]

diff --git a/tool/development/buildbot/.gitignore b/tool/development/buildbot/.gitignore
new file mode 100644 (file)
index 0000000..79f0c4b
--- /dev/null
@@ -0,0 +1 @@
+nativesamples.pyc
\ No newline at end of file
diff --git a/tool/development/buildbot/README b/tool/development/buildbot/README
new file mode 100644 (file)
index 0000000..4859866
--- /dev/null
@@ -0,0 +1,35 @@
+These are build and check scripts related with sample apps development.
+Every change is checked against:
+1. Compilation error using tizen binary from SDK
+2. Compatibility with sample templates using TCC
+3. Code quality using checkpatch_tizen.pl
+
+
+Prerequisites:
+
+
+1. Install pip
+    sudo apt-get install python-pip
+2. Install pygerrit using pip
+    sudo pip install pygerrit
+3. For convenience and easier description of set up process we assume that sample-core-components is installed in ~/
+
+Set up process:
+
+1. Create build directory. In this directory project repositories will be downloaded and checked:
+    mkdir ~/tizen_native_samples_build
+2. Copy sample configuration template to actual samples configuration file:
+    cp ~/sample-core-components/tool/development/buildbot/native-samples.cfg.template ~/tizen_native_samples_build/.native-samples.cfg
+3. Fill fields in ~/tizen_native_samples_build/.native-samples.cfg with correct data.
+    Configuration file has comments which describe configuration options
+4. Test if everything is correct:
+   ~/sample-core-components/tool/development/buildbot/test_configuration.py ~/tizen_native_samples_build/ YOUR_EMAIL@SERVER.COM
+5. Install crontab entry for polling and building changes every minute. Copy line from ~/sample-core-components/tool/development/buildbot/crontab.template
+to users crontab:
+    crontab -e
+
+That's all.
+From now on scripts polls for gerrit changes in projects listed in configuration files.
+Saves those changes to ~/tizen_native_samples_build/.native-samples.db sqlite database.
+Then for every change from database performs build and sanity checks
+and adds comments to gerrit.
\ No newline at end of file
diff --git a/tool/development/buildbot/crontab.template b/tool/development/buildbot/crontab.template
new file mode 100644 (file)
index 0000000..cc98ead
--- /dev/null
@@ -0,0 +1,25 @@
+# Edit this file to introduce tasks to be run by cron.
+#
+# Each task to run has to be defined through a single line
+# indicating with different fields when the task will be run
+# and what command to run for the task
+#
+# To define the time you can provide concrete values for
+# minute (m), hour (h), day of month (dom), month (mon),
+# and day of week (dow) or use '*' in these fields (for 'any').
+# Notice that tasks will be started based on the cron's system
+# daemon's notion of time and timezones.
+#
+# Output of the crontab jobs (including errors) is sent through
+# email to the user the crontab file belongs to (unless redirected).
+#
+# For example, you can run a backup of all your user accounts
+# at 5 a.m every week with:
+# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
+#
+# For more information see the manual pages of crontab(5) and cron(8)
+#
+# m h  dom mon dow   command
+
+* * * * * LC_ALL=en_US.utf-8 /home/user/sample-apps/sample-core-components/tool/development/buildbot/poll_and_build_changes.py /home/user/sample-apps/tizen_native_sample_buildbot 2>&1 >> /home/user/sample-apps/tizen_native_sample_buildbot/poll_and_build_changes.log
+
diff --git a/tool/development/buildbot/native-samples.cfg.template b/tool/development/buildbot/native-samples.cfg.template
new file mode 100644 (file)
index 0000000..8126a3a
--- /dev/null
@@ -0,0 +1,28 @@
+[Gerrit]
+GerritServerName = server.com
+GerritUser = gerrit.user
+GerritPort = 29418
+#List of project in above gerrit server
+#Repositories will be fetched for changes and build
+RepositoryList=apps/wearable/native/sample/app1,apps/wearable/native/sample/app2,apps/mobile/native/sample/app3
+[BuildTools]
+#Path to tizen SDK, wearable and mobile profile rootstraps should be installed
+TizenSdkPath = /home/user/tizen-sdk
+#Path to sample core components repository (we use tcc and checkpatch_tizen tools)
+SampleCoreComponentsPath = /home/user/workspace/sample-core-components
+[EmailNotifications]
+#Email configuration - in case of build script errors or daily routine tests
+SmtpServerName=smtp.mail.domain.com
+SmtpPort=25
+SmtpUser=email.user
+#To create base 64 encoded password use base64 application in linux
+#echo -n password | base64
+SmtpB64EncodedPassword=BASE64ENCODEPASS==
+#This email will be visible 
+MailFrom=Email User <email.user@domain.com>
+#List of receivers
+MailTo=user_to_notify@domain.com
+#List of receiver which will get copy of email
+MailCc=
+#List of receiver which will get blind copy of email
+MailBcc=
diff --git a/tool/development/buildbot/nativesamples.py b/tool/development/buildbot/nativesamples.py
new file mode 100644 (file)
index 0000000..faf9a5d
--- /dev/null
@@ -0,0 +1,428 @@
+# -*- coding: utf-8 -*-
+import sys, os
+import sqlite3
+import ConfigParser
+import subprocess
+import re
+import json
+from pygerrit.ssh import GerritSSHClient
+import smtplib
+import base64
+import traceback
+
+# Import the email modules we'll need
+from email.mime.text import MIMEText
+from email.mime.multipart import MIMEMultipart
+
+reload(sys)
+sys.setdefaultencoding('utf-8')
+
+def numberOfProcesses(pgrepArg):
+       numberOfPollingProcesses = "0"
+       try:
+               numberOfPollingProcesses = subprocess.check_output(["pgrep", "-c", "-f", pgrepArg])
+       except subprocess.CalledProcessError as err:
+               numberOfPollingProcesses = err.output
+       return int(numberOfPollingProcesses)
+
+
+class NativeSamplesConfig:
+       configFilePath = None
+       gerritServerName = None
+       gerritUser = None
+       gerritPort = None
+       gerritSshPrefixUrl = None
+       tizenSdkPath = None
+       sampleCoreComponentsPath = None
+       tizenBinary = None
+       tccBinary = None
+       checkpatchTizenBinary = None
+       convertToSampleProjectBinary = None
+       nativeSampleProjectList = None
+
+       emailNotificationsSmtpServerName = None
+       emailNotificationsSmtpPort = None
+       emailNotificationsSmtpUser = None
+       emailNotificationsSmtpB64EncodedPassword = None
+       emailNotificationsMailFrom = None
+       emailNotificationsMailTo = None
+       emailNotificationsMailCc = None
+       emailNotificationsMailBcc = None
+
+       def __init__(self, configFilePath = None):
+               if configFilePath is not None:
+                       self.readConfigFile(configFilePath)
+       def readConfigFile(self, configFilePath):
+               if os.path.exists(configFilePath):
+                       self.configFilePath = configFilePath
+                       config = ConfigParser.SafeConfigParser({'GerritServerName': None,
+                                                                                                       'GerritUser': None,
+                                                                                                       'GerritPort': None,
+                                                                                                       'TizenSdkPath': None,
+                                                                                                       'SampleCoreComponentsPath': None,
+                                                                                                       'SmtpServerName': None,
+                                                                                                       'SmtpPort': None,
+                                                                                                       'SmtpUser': None,
+                                                                                                       'SmtpB64EncodedPassword': None,
+                                                                                                       'MailFrom': None,
+                                                                                                       'MailTo': None,
+                                                                                                       'MailCc': None,
+                                                                                                       'MailBcc': None})
+                       config.read(configFilePath)
+                       self.gerritServerName =  config.get('Gerrit', 'GerritServerName')
+                       self.gerritUser = config.get('Gerrit', 'GerritUser')
+                       self.gerritPort = config.get('Gerrit', 'GerritPort')
+                       self.tizenSdkPath = config.get('BuildTools', 'TizenSdkPath')
+                       self.sampleCoreComponentsPath =  config.get('BuildTools', 'SampleCoreComponentsPath')
+                       #ssh://[username@]servername[:port]/
+                       self.gerritSshPrefixUrl = "ssh://"
+                       if self.gerritUser is not None:
+                               self.gerritSshPrefixUrl += self.gerritUser + "@"
+                       self.gerritSshPrefixUrl += self.gerritServerName
+                       if self.gerritPort is not None:
+                               self.gerritSshPrefixUrl += ":" + self.gerritPort
+                       self.gerritSshPrefixUrl += "/"
+                       self.nativeSampleProjectList = config.get('Gerrit', 'RepositoryList').split(",")
+                       if os.path.exists(self.tizenSdkPath):
+                               self.tizenBinary = os.path.join(self.tizenSdkPath, "tools/ide/bin/tizen")
+                       if os.path.exists(self.sampleCoreComponentsPath):
+                               self.tccBinary = os.path.join(self.sampleCoreComponentsPath, "tool/tcc.py")
+                               self.convertToSampleProjectBinary = os.path.join(self.sampleCoreComponentsPath, "tool/development/convert-tpk-to-sample-project.sh")
+                               self.checkpatchTizenBinary = os.path.join(self.sampleCoreComponentsPath, "tool/checkpatch_tizen.pl")
+                       self.emailNotificationsSmtpServerName = config.get('EmailNotifications', 'SmtpServerName')
+                       self.emailNotificationsSmtpPort = int(config.get('EmailNotifications', 'SmtpPort'))
+                       self.emailNotificationsSmtpUser = config.get('EmailNotifications', 'SmtpUser')
+                       self.emailNotificationsSmtpB64EncodedPassword = config.get('EmailNotifications', 'SmtpB64EncodedPassword')
+                       self.emailNotificationsMailFrom = config.get('EmailNotifications', 'MailFrom')
+                       self.emailNotificationsMailTo = config.get('EmailNotifications', 'MailTo').split(",")
+                       self.emailNotificationsMailCc = config.get('EmailNotifications', 'MailCc').split(",")
+                       self.emailNotificationsMailBcc = config.get('EmailNotifications', 'MailBcc').split(",")
+               else:
+                       raise ValueError('Config file name:' + configFilePath + " does not exist")
+
+class NativeSample:
+       projectName = None
+       projectPath = None
+       repositoryUrl = None
+       def __init__(self, projectName, projectPath, repositoryUrl):
+               self.projectName = projectName
+               self.projectPath = projectPath
+               self.repositoryUrl = repositoryUrl
+       def __repr__(self):
+               return  ('NativeSample(projectName: %s, ' \
+                                'projectPath: %s, '\
+                                'repositoryUrl = %s)') % (
+                               self.projectName,
+                               self.projectPath,
+                               self.repositoryUrl)
+
+class NativeSampleChange:
+       revisionId = None
+       changeId = None
+       nativeSample = None
+       def __init__(self, nativeSample, revId, changeId = None):
+               self.nativeSample = nativeSample
+               self.revisionId = revId
+               self.changeId = changeId
+       def __repr__(self):
+               return  ('NativeSampleChange(nativeSample: %s, ' \
+                                'revisionId: %s, '\
+                                'changeId = %s)') % (
+                               self.nativeSample,
+                               self.revisionId,
+                               self.changeId)
+
+class NativeSamplesDatabase:
+       createDatabaseScript = """
+       CREATE TABLE IF NOT EXISTS changes_to_evaluate (
+               id INTEGER NOT NULL PRIMARY KEY,
+               project_name TEXT NOT NULL,
+               revision_id TEXT NOT NULL
+       );
+       """
+       insertChangeStatement = """
+       INSERT INTO changes_to_evaluate(project_name, revision_id) VALUES(:project_name, :revision_id)
+       """
+       conn = None
+       databaseFile = None
+       def __init__(self, dbfile):
+               self.databaseFile = dbfile
+               self.conn = sqlite3.connect(dbfile, isolation_level = None)
+               self.conn.execute("PRAGMA busy_timeout = 30000")
+               self.cur = self.conn.cursor()
+               self.cur.executescript(self.createDatabaseScript)
+               self.conn.commit()
+       def saveNativeSampleChange(self, nativeSampleChange):
+               self.cur.execute(self.insertChangeStatement, {'project_name':nativeSampleChange.nativeSample.projectName, 'revision_id': nativeSampleChange.revisionId})
+       def saveNativeSampleChange(self, nativeSampleChange):
+               self.cur.execute(self.insertChangeStatement, {'project_name':nativeSampleChange.nativeSample.projectName, 'revision_id': nativeSampleChange.revisionId})
+       def deleteNativeSampleChange(self, nativeSampleChange):
+               self.cur.execute("DELETE FROM changes_to_evaluate WHERE project_name = :project_name AND revision_id = :revision_id", {'project_name':nativeSampleChange.nativeSample.projectName, 'revision_id': nativeSampleChange.revisionId})
+       def getPendingNativeSampleChanges(self, nativeSamples):
+               ret = []
+               for row in self.cur.execute("SELECT id, project_name, revision_id FROM changes_to_evaluate ORDER BY id ASC"):
+                       ret.append(NativeSampleChange(NativeSample(row[1], None, None), row[2]))
+               return ret
+
+class NativeSampleGerritManager:
+       client = None
+       version = None
+       def __init__(self, config):
+               self.client = GerritSSHClient(config.gerritServerName, username=config.gerritUser, port = int(config.gerritPort), keepalive=True)
+       def getVersion(self):
+               return self.client.run_gerrit_command("version").stdout.read()
+       def getChangeInfo(self, nativeSampleChange):
+               if nativeSampleChange.revisionId is None:
+                       raise ValueError('Native sample revision Id cannot be None for: ' + str(nativeSampleChange))
+               jsonChangeIdStr = self.client.run_gerrit_command("query --format=JSON --current-patch-set=" + nativeSampleChange.revisionId +" project:" + nativeSampleChange.nativeSample.projectName + " limit:1").stdout.read()
+               changeInfo = json.loads(jsonChangeIdStr.split("\n")[0])
+               if 'id' in changeInfo.keys() and 'currentPatchSet' in changeInfo.keys():
+                       return changeInfo
+               else:
+                       return None
+       def addCommentToChange(self, nativeSampleChange, commentText):
+               commentText = commentText.replace("'", " ").replace('"', " ")
+               result = self.client.run_gerrit_command("review -m '\""+commentText+"\"' " + nativeSampleChange.revisionId)
+               print result.stdout.read(), result.stderr.read()
+
+class EmailSender:
+       smtpServer = None
+       smtpPort = None
+       smtpUser = None
+       smtpPassword = None
+       mailFrom = None
+       mailTo = None
+       mailCc = None
+       mailBcc = None
+       def __init__(self, config):
+               self.smtpServer = config.emailNotificationsSmtpServerName
+               self.smtpPort = config.emailNotificationsSmtpPort
+               self.smtpUser = config.emailNotificationsSmtpUser
+               self.smtpPassword = config.emailNotificationsSmtpB64EncodedPassword
+               self.mailFrom = config.emailNotificationsMailFrom
+               self.mailTo = config.emailNotificationsMailTo
+               self.mailCc = config.emailNotificationsMailCc
+               self.mailBcc = config.emailNotificationsMailBcc
+       def send(self, mailSubject, mailText = None, mailTo = None, mailFrom = None, mailCc = None, mailBcc = None):
+               msg = MIMEText(mailText, 'plain', 'utf-8')
+               msg['Subject'] = mailSubject
+               msg['From'] = self.mailFrom
+               msg['To'] = ",".join(mailTo or self.mailTo)
+               msg['CC'] = ",".join(mailCc or self.mailCc)
+               msg['BCC'] = ",".join(mailBcc or self.mailBcc)
+
+               s = smtplib.SMTP(host = self.smtpServer, port = self.smtpPort)
+
+               s.login(self.smtpUser, base64.b64decode(self.smtpPassword))
+               s.sendmail(msg['From'], msg['To'].split(","), msg.as_string())
+               s.quit()
+
+class NativeSamples:
+       samplesList = []
+       config = None
+       nativeSamplesRootPath = None
+       databaseModel = None
+       gerrit = None
+       emailSender = None
+       def __init__(self, rootBuildPath):
+               self.nativeSamplesRootPath = rootBuildPath
+               self.databaseModel = NativeSamplesDatabase(os.path.join(rootBuildPath, ".native-samples.db"))
+               self.config = NativeSamplesConfig(os.path.join(rootBuildPath, ".native-samples.cfg"))
+               self.samplesList = self.config.nativeSampleProjectList
+               self.gerrit = NativeSampleGerritManager(self.config)
+               self.emailSender = EmailSender(self.config)
+       def _cloneSampleFromGerrit(self, nativeSample):
+               if os.path.exists(nativeSample.projectPath):
+                       raise ValueError('Path ' + projectPath + ' of project ' + projectName + ' clone already exist')
+               print "Cloning repository of ", nativeSample
+               subprocess.check_call(["git", "clone", nativeSample.repositoryUrl, nativeSample.projectPath])
+       def _getNativeSample(self, projectName):
+               return NativeSample(projectName, os.path.join(self.nativeSamplesRootPath, projectName), self.config.gerritSshPrefixUrl + projectName)
+
+       def pollForChanges(self, projectList = None):
+               if projectList is None:
+                       projectList = self.samplesList
+               for nativeSampleProjectName in projectList:
+                       nativeSample = self._getNativeSample(nativeSampleProjectName)
+                       if not os.path.exists(nativeSample.projectPath):
+                               self._cloneSampleFromGerrit(nativeSample)
+                       gitCommandList = ["git", "--git-dir=" + os.path.join(nativeSample.projectPath, ".git")]
+                       fetchOutput = subprocess.check_output(gitCommandList + ["fetch", "origin", "refs/changes/*:refs/remotes/origin/gerrit/*"], stderr=subprocess.STDOUT)
+                       if len(fetchOutput) > 0:
+                               print fetchOutput
+                       changeIds = re.findall("(?<=-> ).*", fetchOutput)
+                       if changeIds is None:
+                               continue
+                       for changeId in changeIds:
+                               revisionId = subprocess.check_output(gitCommandList + [ "log", "--pretty=format:%H", "-1", changeId])
+                               self.databaseModel.saveNativeSampleChange(NativeSampleChange(nativeSample, revisionId))
+
+       def buildSampleFromTpkBranch(self, nativeSampleChange, changeInfo = None):
+               print "building tpk for ", nativeSampleChange.nativeSample.projectName, " and changeId:", nativeSampleChange.changeId
+               curDir = os.getcwd()
+               try:
+                       if changeInfo is None:
+                               changeInfo = self.gerrit.getChangeInfo(nativeSampleChange)
+                       os.chdir(nativeSampleChange.nativeSample.projectPath)
+                       if changeInfo['status'] != "MERGED" and changeInfo['status'] != "ABANDONED":
+                               if changeInfo['branch'] == 'tpk':
+                                       print 'building tpk branch'
+                                       if os.path.exists(os.path.join(nativeSampleChange.nativeSample.projectPath, "Build")):
+                                               subprocess.check_call(["rm", "-rf", "Debug"], stderr=subprocess.STDOUT)
+                                               try:
+                                                       subprocess.check_call([self.config.tizenBinary, "clean"], stderr=subprocess.STDOUT)
+                                                       output = subprocess.check_output([self.config.tizenBinary, "build-native", "-a", "arm"], stderr=subprocess.STDOUT)
+                                                       print "SUCCESS: built change " + nativeSampleChange.changeId + " from project " + nativeSampleChange.nativeSample.projectName
+                                                       print output
+                                               except subprocess.CalledProcessError as err:
+                                                       print "FAIL: built change " + nativeSampleChange.changeId + " from project " + nativeSampleChange.nativeSample.projectName
+                                                       print "ERROR:", err.output
+                                                       return False, err.output
+                                               finally:
+                                                       os.chdir(curDir)
+                                       else:
+                                               print 'No Tizen CLI configuration in ' + nativeSampleChange.nativeSample.projectPath
+                               else:
+                                       print "Can't built for branch:" + changeInfo['branch']
+                       else:
+                               print "change already " + changeInfo['status']
+               finally:
+                       os.chdir(curDir)
+               return True, ""
+
+       def checkSampleUsingTcc(self, nativeSampleChange, changeInfo = None):
+               print "checking using tcc tool for ", nativeSampleChange.nativeSample.projectName, " and changeId:", nativeSampleChange.changeId
+               curDir = os.getcwd()
+               try:
+                       if changeInfo is None:
+                               changeInfo = self.gerrit.getChangeInfo(nativeSampleChange)
+
+                       if changeInfo['status'] != "MERGED" and changeInfo['status'] != "ABANDONED":
+                               if changeInfo['branch'] == 'tpk':
+                                       os.chdir(nativeSampleChange.nativeSample.projectPath)
+                                       subprocess.check_call(["git", "checkout", nativeSampleChange.revisionId])
+                                       tccTemplateFile = os.path.join(nativeSampleChange.nativeSample.projectPath, "sample-project-src/.tcc_template_type")
+                                       if not os.path.exists(tccTemplateFile):
+                                               return False, "No file " + str(tccTemplateFile)
+
+                                       templateType = open("sample-project-src/.tcc_template_type").readline()
+                                       templatePath = os.path.join(self.config.sampleCoreComponentsPath, "rule/" + templateType)
+
+                                       subprocess.call(["git", "checkout", "."], stderr=subprocess.STDOUT)
+                                       subprocess.check_call(["git", "clean", "-fdx", "."], stderr=subprocess.STDOUT)
+                                       subprocess.check_call(["git", "checkout", "tizen_2.4"], stderr=subprocess.STDOUT)
+                                       subprocess.check_call([self.config.convertToSampleProjectBinary, "-v", "-r", nativeSampleChange.revisionId], stderr=subprocess.STDOUT)
+
+                                       tccOutput = subprocess.check_output([self.config.tccBinary, "-c", templatePath, "."], stderr=subprocess.STDOUT)
+                                       print tccOutput
+                                       result = re.search("[0-9]+(?= code sections are modified)", tccOutput)
+                                       if int(result.group(0)) > 0:
+                                               return False, result.group(0) + " code sections modified: " + tccOutput
+                                       result = re.search("[0-9]+(?= template files are removed)", tccOutput)
+                                       if int(result.group(0)) > 0:
+                                               return False, result.group(0) + " template files are removed: " + tccOutput
+                               else:
+                                       print "Can't check for branch:" + changeInfo['branch'] + " only tpk"
+                       else:
+                               print "change already " + changeInfo['status']
+               finally:
+                       subprocess.call(["git", "checkout", "."], stderr=subprocess.STDOUT)
+                       subprocess.check_call(["git", "clean", "-fdx", "."], stderr=subprocess.STDOUT)
+                       subprocess.check_call(["git", "checkout", nativeSampleChange.revisionId])
+                       os.chdir(curDir)
+               return True, ""
+       def checkSampleUsingCheckpatchTizen(self, nativeSampleChange, changeInfo):
+               print "checking using checkpatch_tizen.pl", nativeSampleChange.nativeSample.projectName, " and changeId:", nativeSampleChange.changeId
+               curDir = os.getcwd()
+               try:
+                       if changeInfo is None:
+                               changeInfo = self.gerrit.getChangeInfo(nativeSampleChange)
+
+                       if changeInfo['status'] != "MERGED" and changeInfo['status'] != "ABANDONED":
+                               if changeInfo['branch'] == 'tpk':
+                                       os.chdir(nativeSampleChange.nativeSample.projectPath)
+                                       subprocess.check_call(["git", "checkout", "-f", nativeSampleChange.revisionId])
+
+                                       try:
+                                               filesToCheck = []
+                                               for root, dirs, files in os.walk(os.getcwd()):
+                                                       for fileName in files:
+                                                               if fileName.endswith(".c") or fileName.endswith(".h"):
+                                                                       filesToCheck.append(os.path.join(root, fileName))
+                                               checkPatchTizenOutput = subprocess.check_output(["perl", self.config.checkpatchTizenBinary] + filesToCheck, stderr=subprocess.STDOUT)
+                                               print "================================"
+                                               print "SUCCESS: built change " + nativeSampleChange.changeId + " from project " + nativeSampleChange.nativeSample.projectName
+                                               print checkPatchTizenOutput
+                                               print "================================"
+                                       except subprocess.CalledProcessError as err:
+                                               print "================================"
+                                               print "FAIL: checkpatch_tizen.pl of change " + nativeSampleChange.changeId + " from project " + nativeSampleChange.nativeSample.projectName
+                                               print "ERROR:", err.output
+                                               print "================================"
+                                               return False, err.output
+                                       finally:
+                                               os.chdir(curDir)
+
+                               else:
+                                       print "Can't check for branch:" + changeInfo['branch'] + " only tpk"
+                       else:
+                               print "change already " + changeInfo['status']
+               finally:
+                       subprocess.call(["git", "checkout", "."], stderr=subprocess.STDOUT)
+                       subprocess.check_call(["git", "clean", "-fdx", "."], stderr=subprocess.STDOUT)
+                       subprocess.check_call(["git", "checkout", nativeSampleChange.revisionId])
+                       os.chdir(curDir)
+               return True, ""
+       def evaluateChange(self, nativeSampleChange):
+               print 'evaluating change:', nativeSampleChange
+               curDir = os.getcwd()
+               os.chdir(nativeSampleChange.nativeSample.projectPath)
+
+               subprocess.call(["git", "checkout", "."], stderr=subprocess.STDOUT)
+               subprocess.check_call(["git", "clean", "-fdx", "."], stderr=subprocess.STDOUT)
+               subprocess.check_call(["git", "checkout", nativeSampleChange.revisionId])
+
+               changeInfo = self.gerrit.getChangeInfo(nativeSampleChange)
+
+               if changeInfo is not None:
+                       if changeInfo['status'] != "MERGED" and changeInfo['status'] != "ABANDONED":
+                               nativeSampleChange.changeId = changeInfo['id']
+
+                               result, message = self.buildSampleFromTpkBranch(nativeSampleChange, changeInfo)
+                               if result:
+                                       self.gerrit.addCommentToChange(nativeSampleChange, "BuildBot: Compilation successful")
+                               else:
+                                       self.gerrit.addCommentToChange(nativeSampleChange, "BuildBot: Compilation Failed:"+message)
+
+                               result, message = self.checkSampleUsingTcc(nativeSampleChange, changeInfo)
+                               if result:
+                                       self.gerrit.addCommentToChange(nativeSampleChange, "BuildBot: TCC Check successful")
+                               else:
+                                       self.gerrit.addCommentToChange(nativeSampleChange, "BuildBot: TCC Check Failed:"+message)
+
+                               result, message = self.checkSampleUsingCheckpatchTizen(nativeSampleChange, changeInfo)
+                               if result:
+                                       self.gerrit.addCommentToChange(nativeSampleChange, "BuildBot: checkpatch_tizen successful")
+                               else:
+                                       self.gerrit.addCommentToChange(nativeSampleChange, "BuildBot: checkpatch_tizen Failed:"+message)
+                       else:
+                               print "Change already " + changeInfo['status']
+               else:
+                       subject = "Can't find change id for change with revision : " + nativeSampleChange.revisionId + " of project:" + nativeSampleChange.nativeSample.projectName
+                       print subject
+                       self.emailSender.send(mailSubject = subject)
+
+       def evaluatePendingChanges(self):
+               changesList = self.databaseModel.getPendingNativeSampleChanges(self)
+
+               for i in range(len(changesList)):
+                       try:
+                               changesList[i].nativeSample = self._getNativeSample(changesList[i].nativeSample.projectName)
+                               self.evaluateChange(changesList[i])
+                               self.databaseModel.deleteNativeSampleChange(changesList[i])
+                       except:
+                               subject = 'Tizen SAMPLE BUILD SYSTEM error: Something unexpected happened during build process'
+                               stacktrace = "Exception Info:\n\n" + traceback.format_exc()
+                               traceback.print_exc()
+                               self.emailSender.send(mailSubject = subject, mailText = stacktrace)
+                               print "Evaluating next change"
diff --git a/tool/development/buildbot/poll_and_build_changes.py b/tool/development/buildbot/poll_and_build_changes.py
new file mode 100755 (executable)
index 0000000..1214ecd
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+
+import sys, os
+import json
+import argparse
+import subprocess
+import traceback
+import nativesamples as ns
+
+reload(sys)
+sys.setdefaultencoding('utf8')
+
+if ns.numberOfProcesses(".*python.*" + os.path.basename(__file__)) > 1:
+       print "process already running"
+       sys.exit(0)
+
+if len(sys.argv) < 2:
+       print "Please provide samples root build path"
+       sys.exit(0)
+
+rootBuildDir =  os.path.abspath(sys.argv[1])
+
+nativeSamples = ns.NativeSamples(rootBuildDir)
+
+emailSender = ns.EmailSender(nativeSamples.config)
+
+try:
+       nativeSamples.pollForChanges()
+       nativeSamples.evaluatePendingChanges()
+except:
+       subject = 'Tizen SAMPLE BUILD SYSTEM error: Something unexpected happened during build process'
+       stacktrace = "Exception Info:\n\n" + traceback.format_exc()
+       traceback.print_exc()
+       emailSender.send(mailSubject = subject, mailText = stacktrace)
+       print "Evaluating next change"
+       raise
diff --git a/tool/development/buildbot/test_configuration.py b/tool/development/buildbot/test_configuration.py
new file mode 100755 (executable)
index 0000000..3df1b2b
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+
+import sys, os
+import nativesamples as ns
+
+reload(sys)
+sys.setdefaultencoding('utf8')
+
+def checkAndPrintToolStatus(toolPath, toolName):
+       status = "Found "
+       if not os.path.exists(toolPath):
+               status = "Couldn't find "
+
+       print "    ", status, toolName, " in ", toolPath
+
+if len(sys.argv) < 3:
+       print "Please provide samples root build path as first argument and email address as second"
+       sys.exit(0)
+
+print "Number of " + os.path.basename(__file__) + " processes:", ns.numberOfProcesses(".*python.*" + os.path.basename(__file__))
+
+emailTo = sys.argv[2]
+rootBuildDir =  os.path.abspath(sys.argv[1])
+
+nativeSamples = ns.NativeSamples(rootBuildDir)
+
+emailSender = ns.EmailSender(nativeSamples.config)
+
+print ">>> Looking for build and check tools"
+print "Looking for tizen binary:"
+checkAndPrintToolStatus(nativeSamples.config.tizenBinary, "tizen application")
+print "Looking for tcc.py script:"
+checkAndPrintToolStatus(nativeSamples.config.tccBinary, "tcc.py")
+print "Looking for checkpatch_tizen.pl script:"
+checkAndPrintToolStatus(nativeSamples.config.checkpatchTizenBinary, "checkpatch_tizen.pl")
+print "Looking for convert-tpk-to-sample-project.sh script:"
+checkAndPrintToolStatus(nativeSamples.config.convertToSampleProjectBinary, "convert-tpk-to-sample-project.sh")
+print ">>> Checking connection to gerrit:"
+print "Connection OK. Found gerrit version: " + nativeSamples.gerrit.getVersion()
+print ">>> Sending test email"
+emailSubj = "test of samples configuration"
+nativeSamples.emailSender.send(mailSubject = emailSubj, mailTo = [emailTo])
+print "Check if you received email with subject:\"" + emailSubj + "\""
\ No newline at end of file