Catch Error: string from the beginning of the line
[scm/meta/abs.git] / abs
1 #!/usr/bin/env python
2 # vim: ai ts=4 sts=4 et sw=4
3 #
4 # Copyright (c) 2014, 2015, 2016 Samsung Electronics.Co.Ltd.
5 #
6 # This program is free software; you can redistribute it and/or modify it
7 # under the terms of the GNU General Public License as published by the Free
8 # Software Foundation; version 2 of the License
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 # for more details.
14 #
15
16 import sys
17 import os
18 import subprocess
19 import re
20 import argparse
21 from argparse import ArgumentParser
22 import ConfigParser
23 import glob
24 import fnmatch
25 import shutil
26 import zipfile
27 import errno
28
29 g_home = os.path.dirname(os.path.realpath(__file__))
30 class LocalError(Exception):
31     """Local error exception."""
32
33     pass
34
35 class Executor(object):
36     """Subprocess wrapper"""
37
38     def __init__(self, checker=None):
39         self.stdout = subprocess.PIPE
40         self.stderr = subprocess.STDOUT
41         self.checker = checker
42
43     def run(self, cmdline_or_args, show=False, checker=False):
44         """Execute external command"""
45
46         out = ''
47         try:
48             process = subprocess.Popen(cmdline_or_args, \
49                                        stdout=self.stdout, \
50                                        stderr=self.stderr, \
51                                        shell=True)
52             while True:
53                 line = process.stdout.readline()
54                 if show:
55                     print line.rstrip()
56                 out = out + '\n' + line.rstrip()
57                 if not line:
58                     break
59         except:
60             raise LocalError('Running process failed')
61
62         return out
63
64 def list_files(path, ext=None):
65
66     f_list = []
67     for root, dirnames, filenames in os.walk(path):
68         if ext is None:
69             for filename in filenames:
70                 f_list.append(os.path.join(root, filename))
71         else:
72             for filename in fnmatch.filter(filenames, '*.'+ext):
73                 f_list.append(os.path.join(root, filename))
74     return f_list
75
76 class FakeSecHead(object):
77
78     def __init__(self, fp):
79
80         self.fp = fp
81         self.sechead = '[ascection]\n'
82
83     def readline(self):
84
85         if self.sechead:
86             try:
87                 return self.sechead
88             finally:
89                 self.sechead = None
90         else:
91             return self.fp.readline()
92
93 class ErrorParser(object):
94     """Inspect specific error string"""
95
96     parsers = []
97
98     def __init__(self):
99
100         ErrorParser = {'GNU_LINKER':['(.*?):?\(\.\w+\+.*\): (.*)', \
101                                      '(.*[/\\\])?ld(\.exe)?: (.*)'], \
102                        'GNU_GCC':['(.*?):(\d+):(\d+:)? [Ee]rror: ([`\'"](.*)[\'"] undeclared .*)', \
103                                   '(.*?):(\d+):(\d+:)? [Ee]rror: (conflicting types for .*[`\'"](.*)[\'"].*)', \
104                                   '(.*?):(\d+):(\d+:)? (parse error before.*[`\'"](.*)[\'"].*)', \
105                                   '(.*?):(\d+):(\d+:)?\s*(([Ee]rror)|(ERROR)): (.*)'], \
106 #                                  '(.*?):(\d+):(\d+:)? (.*)'], \
107                        'GNU_GMAKE':['(.*):(\d*): (\*\*\* .*)', \
108                                     '.*make.*: \*\*\* .*', \
109                                     '.*make.*: Target (.*) not remade because of errors.', \
110                                     '.*[Cc]ommand not found.*', \
111                                     '^Error:\s*(.*)'], \
112                        'TIZEN_NATIVE':['.*ninja: build stopped.*', \
113                                        'edje_cc: Error..(.*):(\d).*', \
114                                        'edje_cc: Error.*']}
115
116         for parser in ErrorParser:
117             parser_env = os.getenv('SDK_ERROR_'+parser)
118             if parser_env:
119                 self.parsers.append(parser_env)
120             else:
121                 for msg in ErrorParser[parser]:
122                     self.parsers.append(msg)
123
124     def check(self, full_log):
125         """Check error string line by line"""
126
127         #snipset_text = full_log[:full_log.rfind('PLATFORM_VER\t')].split('\n')
128         for line in full_log.split('\n'):
129             errline = re.search('|'.join(self.parsers), line[:1024])
130             if errline:
131                 return errline.string #Errors
132         return None #No error
133
134 class _Rootstrap(object):
135     """Tizen SDK rootstrap info.
136        Used only in Sdk class"""
137
138     rootstrap_list = None
139     sdk_path = None
140
141     def __init__(self, sdk_path=None, config=None, rootstrap_search=None):
142
143         self.tizen = sdk_path
144         self.list_rootstrap(rootstrap_search)
145         self.config_file = config
146
147     def list_rootstrap(self, rootstrap_search=None):
148         """List all the rootstraps"""
149
150         rs_prefix = 'mobile|wearable'
151         if rootstrap_search is not None:
152             rs_prefix = rootstrap_search
153         print 'Set rs_prefix: %s' % rs_prefix
154
155         if self.rootstrap_list != None:
156             return self.rootstrap_list
157
158         cmdline = self.tizen + ' list rootstrap'
159         ret = Executor().run(cmdline, show=False)
160         for x in ret.splitlines():
161             if re.search('(%s)-([0-9.]*)-(device.*|emulator.*).core.*' % rs_prefix, x):
162                 if self.rootstrap_list == None:
163                     self.rootstrap_list = []
164                 self.rootstrap_list.append(x.split(' ')[0])
165             else:
166                 print 'No search result for %s' % '(%s)-([0-9.]*)-(device.*|emulator.*).core.*' % rs_prefix
167         return self.rootstrap_list
168
169     def check_rootstrap(self, rootstrap, show=True):
170         """Specific rootstrap is in the SDK
171            Otherwise use default"""
172
173         if rootstrap == None:
174             rootstrap = 'mobile-3.0-emulator.core' #default
175         if rootstrap in self.rootstrap_list:
176             return rootstrap
177         else:
178             if show == True:
179                 print '  ERROR: Rootstrap [%s] does not exist' % rootstrap
180                 print '  Update your rootstrap or use one of:\n    %s' \
181                        % '\n    '.join(self.list_rootstrap())
182             return None
183
184 class Sdk(object):
185     """Tizen SDK related job"""
186
187     rs = None #_Rootstrap class instance
188     rootstrap_list = None
189     sdk_to_search = ['tizen-studio/tools/ide/bin/tizen', \
190                      'tizen-sdk/tools/ide/bin/tizen', \
191                      'tizen-sdk-ux/tools/ide/bin/tizen', \
192                      'tizen-sdk-cli/tools/ide/bin/tizen']
193
194     def __init__(self, sdkpath=None, rootstrap_search=None):
195
196         self.error_parser = ErrorParser()
197         self.runtool = Executor(checker=self.error_parser)
198
199         self.home = os.getenv('HOME')
200         self.config_file = os.path.join(g_home, '.abs')
201
202         if sdkpath is None:
203             self.tizen = self.get_user_root()
204             if self.tizen is None or self.tizen == '':
205                 for i in self.sdk_to_search:
206                     if os.path.isfile(os.path.join(self.home, i)):
207                         self.tizen = os.path.join(self.home, i)
208                         break
209         else:
210             self.tizen = os.path.join(sdkpath, 'tools/ide/bin/tizen')
211             self.update_user_root(self.tizen)
212
213         if not os.path.isfile(self.tizen):
214             print 'Cannot locate cli tool'
215             raise LocalError('Fail to locate cli tool')
216
217         self.rs = _Rootstrap(sdk_path=self.tizen, config=self.config_file, rootstrap_search=rootstrap_search)
218
219     def get_user_root(self):
220
221         if os.path.isfile(self.config_file):
222             config = ConfigParser.RawConfigParser()
223             config.read(self.config_file)
224             return config.get('Global', 'tizen')
225         return None
226
227     def update_user_root(self, path):
228
229         if not os.path.isfile(self.config_file):
230             with open(self.config_file, 'w') as f:
231                 f.write('[Global]\n')
232                 f.write('tizen = %s\n' % path)
233             return
234
235         config = ConfigParser.RawConfigParser()
236         config.read(self.config_file)
237         config.set('Global', 'tizen', path)
238         with open(self.config_file, 'wb') as cf:
239             config.write(cf)
240
241     def list_rootstrap(self):
242         return self.rs.list_rootstrap()
243
244     def check_rootstrap(self, rootstrap):
245         return self.rs.check_rootstrap(rootstrap)
246
247     def _run(self, command, args, show=True, checker=False):
248         """Run a tizen command"""
249
250         cmd = [self.tizen, command] + args
251         print '\nRunning command:\n    %s' % ' '.join(cmd)
252         return self.runtool.run(' '.join(cmd), show=show, checker=checker)
253
254     def copytree2(self, src, dst, symlinks=False, ignore=None):
255         """Copy with Ignore & Overwrite"""
256         names = os.listdir(src)
257         if ignore is not None:
258             ignored_names = ignore(src, names)
259         else:
260             ignored_names = set()
261
262         try:
263             os.makedirs(dst)
264         except:
265             pass
266
267         errors = []
268         for name in names:
269             if name in ignored_names:
270                 continue
271             srcname = os.path.join(src, name)
272             dstname = os.path.join(dst, name)
273             try:
274                 if symlinks and os.path.islink(srcname):
275                     linkto = os.readlink(srcname)
276                     os.symlink(linkto, dstname)
277                 elif os.path.isdir(srcname):
278                     self.copytree2(srcname, dstname, symlinks, ignore)
279                 else:
280                     # Will raise a SpecialFileError for unsupported file types
281                     shutil.copy2(srcname, dstname)
282             # catch the Error from the recursive copytree so that we can
283             # continue with other files
284             except shutil.Error, err:
285                 errors.extend(err.args[0])
286             except EnvironmentError, why:
287                 errors.append((srcname, dstname, str(why)))
288         try:
289             shutil.copystat(src, dst)
290         except OSError, why:
291             if WindowsError is not None and isinstance(why, WindowsError):
292                 # Copying file access times may fail on Windows
293                 pass
294             else:
295                 errors.append((src, dst, str(why)))
296         #if errors:
297          #   raise shutil.Error, errors
298
299     def _copy_build_output(self, src, dst):
300         if not os.path.isdir(src) :
301             return
302         try:
303             self.copytree2(src, dst, ignore=shutil.ignore_patterns('*.edc', '*.po', 'objs', '*.info', '*.so', 'CMakeLists.txt', '*.h', '*.c'))
304         except OSError as exc:
305             # File already exist
306             if exc.errno == errno.EEXIST:
307                 shutil.copy(src, dst)
308             if exc.errno == errno.ENOENT:
309                 shutil.copy(src, dst)
310             else:
311                 raise
312
313     def _package_sharedlib(self, project_path, conf, app_name):
314         """If -r option used for packaging, make zip file from copied files"""
315         #project_path=project['path']
316         project_build_output_path=os.path.join(project_path, conf)
317         package_path=os.path.join(project_build_output_path, '.pkg')
318
319         if os.path.isdir(package_path):
320             shutil.rmtree(package_path)
321         os.makedirs(package_path)
322         os.makedirs(os.path.join(package_path, 'lib'))
323
324         #Copy project resource
325         self._copy_build_output(os.path.join(project_path, 'lib'), os.path.join(package_path, 'lib'))
326         self._copy_build_output(os.path.join(project_path, 'res'), os.path.join(package_path, 'res'))
327
328         #Copy built res resource
329         self._copy_build_output(os.path.join(project_build_output_path, 'res'), os.path.join(package_path, 'res'))
330
331         #Copy so library file
332         for filename in list_files(project_build_output_path, 'so'):
333             shutil.copy(filename, os.path.join(package_path, 'lib'))
334
335         # Copy so library file
336         zipname=app_name + '.zip'
337         rsrc_zip = os.path.join(project_build_output_path, zipname)
338         myZipFile = zipfile.ZipFile(rsrc_zip, 'w')
339         for filename in list_files(package_path):
340             try:
341                 myZipFile.write(filename, filename.replace(package_path, ''))
342             except Exception, e:
343                 print str(e)
344         myZipFile.close()
345         return rsrc_zip
346
347     def build_tizen(self, source, rootstrap=None, arch=None, conf='Debug', jobs=None):
348         """SDK CLI build command"""
349
350         _rootstrap = self.check_rootstrap(rootstrap)
351         if _rootstrap == None:
352             raise LocalError('Rootstrap %s not exist' % rootstrap)
353
354         if rootstrap is None and arch is None:
355             rootstrap = _rootstrap
356             self.arch = 'x86'
357         elif arch is None:
358             if 'emulator64' in rootstrap: self.arch = 'x86_64'
359             elif 'device64' in rootstrap: self.arch = 'aarch64'
360             elif 'emulator' in rootstrap: self.arch = 'x86'
361             elif 'device' in rootstrap: self.arch = 'arm'
362         elif rootstrap is None:
363             if arch not in ['x86', 'arm', 'aarch64', 'x86_64']:
364                 raise LocalError('Architecture and rootstrap mismatch')
365
366             rootstrap = _rootstrap
367             if arch == 'x86_64': rootstrap = rootstrap.replace('emulator', 'emulator64')
368             elif arch == 'aarch64': rootstrap = rootstrap.replace('emulator', 'device64')
369             elif arch == 'arm': rootstrap = rootstrap.replace('emulator', 'device')
370
371         for x in source.project_list:
372             b_args = []
373             if x['web_app'] == True:
374                 print '\n\n BUILD WEB\n'
375                 b_args.extend(['--', x['path']])
376                 out = self._run('build-web ', b_args, checker=True)
377             else:
378                 print '\n\n BUILD NATIVE\n'
379                 if jobs is not None:
380                     b_args.extend(['-j', jobs])
381                 b_args.extend(['-r', rootstrap, '-a', self.arch, '-C', conf])
382                 b_args.extend(['--', x['path']])
383                 out = self._run('build-native', b_args, checker=True)
384             logpath = os.path.join(source.output_dir, \
385                                   'build_%s_%s' % (rootstrap, os.path.basename(x['path'])))
386             if not os.path.isdir(source.output_dir):
387                 os.makedirs(source.output_dir)
388             with open(logpath, 'w') as lf:
389                 lf.write(out)
390             ret = self.error_parser.check(out)
391             if True:
392                 with open(logpath+'.log', 'w') as lf:
393                     lf.write(out)
394             if ret:
395                 raise LocalError(ret)
396
397     def package(self, source, cert=None, pkg_type=None, conf='Debug', manual_strip=False):
398         """SDK CLI package command
399             IF Debug + Manual Strip off then generate package-name-debug.tpk
400             IF Debug + Manual Strip on then generate package-name.tpk with custom strip
401             IF Release then generate package-name.tpk with strip option
402         """
403         if cert is None: cert = 'ABS'
404         if pkg_type is None: pkg_type = 'tpk'
405         if conf is None: conf = 'Debug'
406
407         final_app = ''
408         main_args = ['-t', pkg_type, '-s', cert]
409         main_args_web = ['-t', 'wgt', '-s', cert]
410         out = '' #logfile
411
412         # remove tpk or zip file on project path
413         package_list = []
414         for i, x in enumerate(source.project_list):
415             package_list.extend(list_files(os.path.join(x['path'], conf), ext='tpk'))
416             package_list.extend(list_files(os.path.join(x['path'], conf), ext='zip'))
417             package_list.extend(list_files(x['path'], ext='wgt'))
418
419         for k in package_list :
420             print ' package list ' + k;
421             os.remove(k)
422
423         # Manual strip
424         if manual_strip == True :
425             main_args.extend(['--strip', 'on'])
426             strip_cmd='';
427             if self.arch == None:
428                 raise LocalError('Architecture is None')
429
430             for i, x in enumerate(source.project_list):
431                 dir = os.path.join(x['path'], conf)
432                 if not os.path.isdir(dir):
433                     continue
434                 files = [os.path.join(dir,f) for f in os.listdir(dir) if os.path.isfile(os.path.join(dir,f))]
435
436                 # dir must be "project/Debug directory"
437                 info_path = os.path.join(dir, "build.info")
438                 # Default Strip gcc version is 6.2
439                 gcc_version = "6.2"
440                 # Parsing GCC version from build.info inside Debug directory in Project to each project
441                 if os.path.exists(info_path) :
442                     with open(info_path) as fp :
443                         for line in fp :
444                             if line.startswith("toolchain=") :
445                                 line = line.strip()
446                                 gcc_version = re.findall("\d+\.\d+", line)[0]
447                 else :
448                     print "Cannot find Debug/build.info. The default gcc will strip tpk"
449
450                 print "gcc version:" + gcc_version
451
452                 if self.arch == 'x86' :
453                     if(gcc_version == "4.9"):
454                         strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../i386-linux-gnueabi-gcc-' + gcc_version + '/bin/i386-linux-gnueabi-strip')
455                     else:
456                         strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../i586-linux-gnueabi-gcc-' + gcc_version + '/bin/i586-linux-gnueabi-strip')
457                 elif self.arch == 'arm' :
458                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../arm-linux-gnueabi-gcc-' + gcc_version + '/bin/arm-linux-gnueabi-strip')
459                 elif self.arch == 'x86_64' :
460                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../x86_64-linux-gnu-gcc-' + gcc_version + '/bin/x86_64-linux-gnu-strip')
461                 elif self.arch == 'aarch64' :
462                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../aarch64-linux-gnu-gcc-' + gcc_version + '/bin/aarch64-linux-gnu-strip')
463
464                 print strip_cmd
465
466                 for k in files:
467                     cmdline = strip_cmd + ' ' + k;
468                     Executor().run(cmdline, show=False)
469
470         elif conf == 'Release':
471             main_args.extend(['--strip', 'on'])
472
473         for i, x in enumerate(source.project_list):
474             if x['web_app'] == False:
475                 if x['type'] == 'app':
476                     print '\n\n PACKAGE NATIVE\n'
477                     out = '%s\n%s' % (out, \
478                           self._run('package', main_args + ['--',os.path.join(x['path'],conf)]))
479                     try:
480                         final_app = list_files(os.path.join(x['path'], conf), ext='tpk')[0]
481                     except:
482                         raise LocalError('TPK file not generated for %s.' % x['APPNAME'])
483                     x['out_package'] = final_app
484                 elif x['type'] == 'sharedLib':
485                     self._package_sharedlib(x['path'], conf, x['APPNAME'])
486                     x['out_package'] = list_files(os.path.join(x['path'], conf), ext='zip')[0]
487                 else:
488                     raise LocalError('Not supported project type %s' % x['type'])
489             elif x['web_app'] == True:
490                 print '\n\n PACKAGE WEB\n'
491                 out = '%s\n%s' % (out, \
492                       self._run('package', main_args_web + ['--', os.path.join(x['path'], '.buildResult')]))
493                 try:
494                     final_app = list_files(os.path.join(x['path'], '.buildResult'), ext='wgt')[0]
495                 except:
496                     raise LocalError('WGT file not generated for %s.' % x['APPNAME'])
497                 x['out_package'] = final_app
498
499         if source.b_multi == True:
500             extra_args=[]
501             print 'THIS IS MULTI PROJECT'
502             for i, x in enumerate(source.project_list):
503                 if x['out_package'] != final_app and x.get('type') == 'app':
504                     extra_args.extend(['-r', '"%s"' % x['out_package']])
505                 elif x.get('type') == 'sharedLib':
506                     extra_args.extend(['-r', '"%s"' % x['out_package']])
507
508             extra_args.extend(['--', '"%s"' % final_app])
509             if final_app.endswith('.tpk'):
510                 out = self._run('package', main_args + extra_args)
511             elif final_app.endswith('.wgt'):
512                 out = self._run('package', main_args_web + extra_args)
513
514         #TODO: signature validation check failed : Invalid file reference. An unsigned file was found.
515         if final_app.endswith('.tpk'):
516             print 'Packaging final step again!'
517             out = self._run('package', main_args + ['--', '"%s"' % final_app])
518
519         #Append arch to web binary
520         if final_app.endswith('.wgt'):
521             final_app_with_arch = final_app.replace('.wgt', '-%s.wgt' % self.arch)
522             os.rename(final_app, final_app_with_arch)
523             final_app = final_app_with_arch
524
525         #Copy tpk to output directory
526         if conf == 'Debug' and manual_strip == False :
527             basename = os.path.splitext(final_app)[0]
528             if final_app.endswith('.tpk'):
529                 newname = basename +'-debug.tpk'
530             elif final_app.endswith('.wgt'):
531                 newname = basename +'-debug.wgt'
532             os.rename(final_app, newname)
533             shutil.copy(newname, source.output_dir)
534         else :
535             shutil.copy(final_app, source.output_dir)
536
537     def clean(self, source):
538         """SDK CLI clean command"""
539
540         if os.path.isdir(source.multizip_path):
541             shutil.rmtree(source.multizip_path)
542
543         if os.path.isfile(os.path.join(source.multizip_path, '.zip')):
544             os.remove(os.path.join(source.multizip_path, '.zip'))
545
546         for x in source.project_list:
547             self._run('clean', ['--', x['path']], show=False)
548
549 class Source(object):
550     """Project source related job"""
551
552     workspace = '' #Project root directory
553     project_list = []
554     b_multi = False
555     multi_conf_file = 'WORKSPACE' #Assume multi-project if this file exist.
556     multizip_path = '' #For multi-project packaging -r option
557     property_dict = {}
558     output_dir = '_abs_out_'
559
560     def __init__(self, src=None):
561
562         if src == None:
563             self.workspace = os.getcwd()
564         else:
565             self.workspace = os.path.abspath(src)
566         self.output_dir = os.path.join(self.workspace, self.output_dir)
567
568         os.environ['workspace_loc']=str(os.path.realpath(self.workspace))
569
570         self.multizip_path = os.path.join(self.workspace, 'multizip')
571         self.pre_process()
572
573     def set_properties(self, path):
574         """Fetch all properties from project_def.prop"""
575
576         mydict = {}
577         cp = ConfigParser.SafeConfigParser()
578         cp.optionxform = str
579         if self.is_web_app(path):
580             mydict['web_app'] = True
581         else:
582             mydict['web_app'] = False
583             cp.readfp(FakeSecHead(open(os.path.join(path, 'project_def.prop'))))
584             for x in cp.items('ascection'):
585                 mydict[x[0]] = x[1]
586         mydict['path'] = path
587         return mydict
588
589     def set_user_options(self, c_opts=None, cpp_opts=None, link_opts=None):
590         if c_opts is not None:
591             os.environ['USER_C_OPTS'] = c_opts
592             print 'Set USER_C_OPTS=[%s]' % os.getenv('USER_C_OPTS')
593         if cpp_opts is not None:
594             os.environ['USER_CPP_OPTS'] = cpp_opts
595             print 'Set USER_CPP_OPTS=[%s]' % os.getenv('USER_CPP_OPTS')
596         if link_opts is not None:
597             os.environ['USER_LINK_OPTS'] = link_opts
598             print 'Set USER_LINK_OPTS=[%s]' % os.getenv('USER_LINK_OPTS')
599
600     def is_web_app(self, project_directory):
601         if os.path.isfile(os.path.join(project_directory, 'config.xml')):
602             return True
603         return False
604
605     def pre_process(self):
606
607         if os.path.isfile(os.path.join(self.workspace, self.multi_conf_file)):
608             self.b_multi = True
609             with open(os.path.join(self.workspace, self.multi_conf_file)) as f:
610                 for line in f:
611                     if not line.strip():
612                         continue
613                     file_path = os.path.join(self.workspace, line.rstrip())
614                     self.project_list.append(self.set_properties(file_path))
615         else:
616             self.b_multi = False
617             file_path = os.path.join(self.workspace)
618             self.project_list.append(self.set_properties(file_path))
619
620 def argument_parsing(argv):
621     """Any arguments passed from user"""
622
623     parser = argparse.ArgumentParser(description='ABS command line interface')
624
625     subparsers = parser.add_subparsers(dest='subcommands')
626
627     #### [subcommand - BUILD] ####
628     build = subparsers.add_parser('build')
629     build.add_argument('-w', '--workspace', action='store', dest='workspace', \
630                         help='source directory')
631     build.add_argument('-r', '--rootstrap', action='store', dest='rootstrap', \
632                         help='(ex, mobile-3.0-device.core) rootstrap name')
633     build.add_argument('-a', '--arch', action='store', dest='arch', \
634                         help='(x86|arm|x86_64|aarch64) Architecture to build')
635     build.add_argument('-t', '--type', action='store', dest='type', \
636                         help='(tpk|wgt) Packaging type')
637     build.add_argument('-s', '--cert', action='store', dest='cert', \
638                         help='(ex, ABS) Certificate profile name')
639     build.add_argument('-c', '--conf', action='store',default='Release', dest='conf', \
640                         help='(ex, Debug|Release) Build Configuration')
641     build.add_argument('-j', '--jobs', action='store', dest='jobs', \
642                         help='(number of jobs) The number of parallel builds')
643     build.add_argument('--sdkpath', action='store', dest='sdkpath', \
644                         help='Specify Tizen SDK installation root (one time init).' \
645                              ' ex) /home/yours/tizen-sdk/')
646     build.add_argument('--profile-to-search', action='store', dest='profiletosearch', \
647                         help='Rootstrap profile prefix.' \
648                              ' ex) (mobile|wearable|da-hfp)')
649     build.add_argument('--c-opts', action='store', dest='c_opts', \
650                         help='Extra compile options USER_C_OPTS')
651     build.add_argument('--cpp-opts', action='store', dest='cpp_opts', \
652                         help='Extra compile options USER_CPP_OPTS')
653     build.add_argument('--link-opts', action='store', dest='link_opts', \
654                         help='Extra linking options USER_LINK_OPTS')
655
656     return parser.parse_args(argv[1:])
657
658 def build_main(args):
659     """Command [build] entry point."""
660
661     try:
662         my_source = Source(src=args.workspace)
663
664         my_source.set_user_options(c_opts=args.c_opts, cpp_opts=args.cpp_opts, link_opts=args.link_opts)
665         print '-------------------'
666         print '(%s)' % args.profiletosearch
667         print '-------------------'
668         my_sdk = Sdk(sdkpath=args.sdkpath, rootstrap_search=args.profiletosearch)
669         my_sdk.clean(my_source)
670         my_sdk.build_tizen(my_source, rootstrap=args.rootstrap, arch=args.arch, jobs=args.jobs)
671         if args.conf == 'Debug' :
672             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert)
673             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert, manual_strip=True)
674         else :
675             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert, manual_strip=True)
676
677     except Exception as err:
678         wrk = os.path.join(os.path.abspath(args.workspace), '_abs_out_')
679         if not os.path.isdir(wrk):
680             os.makedirs(wrk)
681         with open(os.path.join(wrk, 'build_EXCEPTION.log'), 'w') as ef:
682             ef.write('Exception %s' % str(err))
683         raise err
684
685 def main(argv):
686     """Script entry point."""
687
688     args = argument_parsing(argv)
689
690     if args.subcommands == 'build':
691         return build_main(args)
692     else:
693         print 'Unsupported command %s' % args.subcommands
694         raise LocalError('Command %s not supported' % args.subcommands)
695
696 if __name__ == '__main__':
697
698     try:
699         sys.exit(main(sys.argv))
700     except Exception, e:
701         print 'Exception %s' % str(e)
702         #FIXME: Remove hard-coded output directory.
703         if not os.path.isdir('_abs_out_'):
704             os.makedirs('_abs_out_')
705         with open(os.path.join('_abs_out_', 'build_EXCEPTION.log'), 'w') as ef:
706             ef.write('Exception %s' % repr(e))
707         sys.exit(1)