fix
[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         if command == 'package':
252             cmd = ['dbus-run-session -- bash; echo build | gnome-keyring-daemon --unlock; '] + cmd
253         print '\nRunning command:\n    %s' % ' '.join(cmd)
254         return self.runtool.run('{}'.format(' '.join(cmd)), \
255                 show=show, checker=checker)
256
257     def copytree2(self, src, dst, symlinks=False, ignore=None):
258         """Copy with Ignore & Overwrite"""
259         names = os.listdir(src)
260         if ignore is not None:
261             ignored_names = ignore(src, names)
262         else:
263             ignored_names = set()
264
265         try:
266             os.makedirs(dst)
267         except:
268             pass
269
270         errors = []
271         for name in names:
272             if name in ignored_names:
273                 continue
274             srcname = os.path.join(src, name)
275             dstname = os.path.join(dst, name)
276             try:
277                 if symlinks and os.path.islink(srcname):
278                     linkto = os.readlink(srcname)
279                     os.symlink(linkto, dstname)
280                 elif os.path.isdir(srcname):
281                     self.copytree2(srcname, dstname, symlinks, ignore)
282                 else:
283                     # Will raise a SpecialFileError for unsupported file types
284                     shutil.copy2(srcname, dstname)
285             # catch the Error from the recursive copytree so that we can
286             # continue with other files
287             except shutil.Error, err:
288                 errors.extend(err.args[0])
289             except EnvironmentError, why:
290                 errors.append((srcname, dstname, str(why)))
291         try:
292             shutil.copystat(src, dst)
293         except OSError, why:
294             if WindowsError is not None and isinstance(why, WindowsError):
295                 # Copying file access times may fail on Windows
296                 pass
297             else:
298                 errors.append((src, dst, str(why)))
299         #if errors:
300          #   raise shutil.Error, errors
301
302     def _copy_build_output(self, src, dst):
303         if not os.path.isdir(src) :
304             return
305         try:
306             self.copytree2(src, dst, ignore=shutil.ignore_patterns('*.edc', '*.po', 'objs', '*.info', '*.so', 'CMakeLists.txt', '*.h', '*.c'))
307         except OSError as exc:
308             # File already exist
309             if exc.errno == errno.EEXIST:
310                 shutil.copy(src, dst)
311             if exc.errno == errno.ENOENT:
312                 shutil.copy(src, dst)
313             else:
314                 raise
315
316     def _package_sharedlib(self, project_path, conf, app_name):
317         """If -r option used for packaging, make zip file from copied files"""
318         #project_path=project['path']
319         project_build_output_path=os.path.join(project_path, conf)
320         package_path=os.path.join(project_build_output_path, '.pkg')
321
322         if os.path.isdir(package_path):
323             shutil.rmtree(package_path)
324         os.makedirs(package_path)
325         os.makedirs(os.path.join(package_path, 'lib'))
326
327         #Copy project resource
328         self._copy_build_output(os.path.join(project_path, 'lib'), os.path.join(package_path, 'lib'))
329         self._copy_build_output(os.path.join(project_path, 'res'), os.path.join(package_path, 'res'))
330
331         #Copy built res resource
332         self._copy_build_output(os.path.join(project_build_output_path, 'res'), os.path.join(package_path, 'res'))
333
334         #Copy so library file
335         for filename in list_files(project_build_output_path, 'so'):
336             shutil.copy(filename, os.path.join(package_path, 'lib'))
337
338         # Copy so library file
339         zipname=app_name + '.zip'
340         rsrc_zip = os.path.join(project_build_output_path, zipname)
341         myZipFile = zipfile.ZipFile(rsrc_zip, 'w')
342         for filename in list_files(package_path):
343             try:
344                 myZipFile.write(filename, filename.replace(package_path, ''))
345             except Exception, e:
346                 print str(e)
347         myZipFile.close()
348         return rsrc_zip
349
350     def build_tizen(self, source, rootstrap=None, arch=None, conf='Debug', jobs=None):
351         """SDK CLI build command"""
352
353         _rootstrap = self.check_rootstrap(rootstrap)
354         if _rootstrap == None:
355             raise LocalError('Rootstrap %s not exist' % rootstrap)
356
357         if rootstrap is None and arch is None:
358             rootstrap = _rootstrap
359             self.arch = 'x86'
360         elif arch is None:
361             if 'emulator64' in rootstrap: self.arch = 'x86_64'
362             elif 'device64' in rootstrap: self.arch = 'aarch64'
363             elif 'emulator' in rootstrap: self.arch = 'x86'
364             elif 'device' in rootstrap: self.arch = 'arm'
365         elif rootstrap is None:
366             if arch not in ['x86', 'arm', 'aarch64', 'x86_64']:
367                 raise LocalError('Architecture and rootstrap mismatch')
368
369             rootstrap = _rootstrap
370             if arch == 'x86_64': rootstrap = rootstrap.replace('emulator', 'emulator64')
371             elif arch == 'aarch64': rootstrap = rootstrap.replace('emulator', 'device64')
372             elif arch == 'arm': rootstrap = rootstrap.replace('emulator', 'device')
373
374         for x in source.project_list:
375             b_args = []
376             if x['web_app'] == True:
377                 print '\n\n BUILD WEB\n'
378                 b_args.extend(['--', x['path']])
379                 out = self._run('build-web ', b_args, checker=True)
380             else:
381                 print '\n\n BUILD NATIVE\n'
382                 if jobs is not None:
383                     b_args.extend(['-j', jobs])
384                 b_args.extend(['-r', rootstrap, '-a', self.arch, '-C', conf])
385                 b_args.extend(['--', x['path']])
386                 out = self._run('build-native', b_args, checker=True)
387             logpath = os.path.join(source.output_dir, \
388                                   'build_%s_%s' % (rootstrap, os.path.basename(x['path'])))
389             if not os.path.isdir(source.output_dir):
390                 os.makedirs(source.output_dir)
391             with open(logpath, 'w') as lf:
392                 lf.write(out)
393             ret = self.error_parser.check(out)
394             if True:
395                 with open(logpath+'.log', 'w') as lf:
396                     lf.write(out)
397             if ret:
398                 raise LocalError(ret)
399
400     def raise_package_exception(self, output_dir, i, bname, appname):
401         logpath = os.path.join(output_dir, \
402                               'package_%d_%s' % (i, bname))
403         if not os.path.isdir(output_dir):
404             os.makedirs(output_dir)
405         with open(logpath + '.log', 'w') as lf:
406             lf.write(out)
407         raise LocalError('TPK/WGT file not generated for %s.' % appname)
408
409     def package(self, source, cert=None, pkg_type=None, conf='Debug', manual_strip=False):
410         """SDK CLI package command
411             IF Debug + Manual Strip off then generate package-name-debug.tpk
412             IF Debug + Manual Strip on then generate package-name.tpk with custom strip
413             IF Release then generate package-name.tpk with strip option
414         """
415         if cert is None: cert = 'ABS'
416         if pkg_type is None: pkg_type = 'tpk'
417         if conf is None: conf = 'Debug'
418
419         final_app = ''
420         main_args = ['-t', pkg_type, '-s', cert]
421         main_args_web = ['-t', 'wgt', '-s', cert]
422         out = '' #logfile
423
424         # remove tpk or zip file on project path
425         package_list = []
426         for i, x in enumerate(source.project_list):
427             package_list.extend(list_files(os.path.join(x['path'], conf), ext='tpk'))
428             package_list.extend(list_files(os.path.join(x['path'], conf), ext='zip'))
429             package_list.extend(list_files(x['path'], ext='wgt'))
430
431         for k in package_list :
432             print ' package list ' + k;
433             os.remove(k)
434
435         # Manual strip
436         if manual_strip == True :
437             main_args.extend(['--strip', 'on'])
438             strip_cmd='';
439             if self.arch == None:
440                 raise LocalError('Architecture is None')
441
442             for i, x in enumerate(source.project_list):
443                 dir = os.path.join(x['path'], conf)
444                 if not os.path.isdir(dir):
445                     continue
446                 files = [os.path.join(dir,f) for f in os.listdir(dir) if os.path.isfile(os.path.join(dir,f))]
447
448                 # dir must be "project/Debug directory"
449                 info_path = os.path.join(dir, "build.info")
450                 # Default Strip gcc version is 6.2
451                 gcc_version = "6.2"
452                 # Parsing GCC version from build.info inside Debug directory in Project to each project
453                 if os.path.exists(info_path) :
454                     with open(info_path) as fp :
455                         for line in fp :
456                             if line.startswith("toolchain=") :
457                                 line = line.strip()
458                                 gcc_version = re.findall("\d+\.\d+", line)[0]
459                 else :
460                     print "Cannot find Debug/build.info. The default gcc will strip tpk"
461
462                 print "gcc version:" + gcc_version
463
464                 if self.arch == 'x86' :
465                     if(gcc_version == "4.9"):
466                         strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../i386-linux-gnueabi-gcc-' + gcc_version + '/bin/i386-linux-gnueabi-strip')
467                     else:
468                         strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../i586-linux-gnueabi-gcc-' + gcc_version + '/bin/i586-linux-gnueabi-strip')
469                 elif self.arch == 'arm' :
470                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../arm-linux-gnueabi-gcc-' + gcc_version + '/bin/arm-linux-gnueabi-strip')
471                 elif self.arch == 'x86_64' :
472                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../x86_64-linux-gnu-gcc-' + gcc_version + '/bin/x86_64-linux-gnu-strip')
473                 elif self.arch == 'aarch64' :
474                     strip_cmd = os.path.join(os.path.dirname(self.tizen), '../../aarch64-linux-gnu-gcc-' + gcc_version + '/bin/aarch64-linux-gnu-strip')
475
476                 print strip_cmd
477
478                 for k in files:
479                     cmdline = strip_cmd + ' ' + k;
480                     Executor().run(cmdline, show=False)
481
482         elif conf == 'Release':
483             main_args.extend(['--strip', 'on'])
484
485         for i, x in enumerate(source.project_list):
486             if x['web_app'] == False:
487                 if x['type'] == 'app':
488                     print '\n\n PACKAGE NATIVE\n'
489                     out = '%s\n%s' % (out, \
490                           self._run('package', main_args + ['--',os.path.join(x['path'],conf)]))
491                     try:
492                         final_app = list_files(os.path.join(x['path'], conf), ext='tpk')[0]
493                     except:
494                         self.raise_package_exception(self.output_dir, i, os.path.basename(x['path']), x['APPNAME'])
495                     x['out_package'] = final_app
496                 elif x['type'] == 'sharedLib':
497                     self._package_sharedlib(x['path'], conf, x['APPNAME'])
498                     x['out_package'] = list_files(os.path.join(x['path'], conf), ext='zip')[0]
499                 else:
500                     raise LocalError('Not supported project type %s' % x['type'])
501             elif x['web_app'] == True:
502                 print '\n\n PACKAGE WEB\n'
503                 out = '%s\n%s' % (out, \
504                       self._run('package', main_args_web + ['--', os.path.join(x['path'], '.buildResult')]))
505                 try:
506                     final_app = list_files(os.path.join(x['path'], '.buildResult'), ext='wgt')[0]
507                 except:
508                     self.raise_package_exception(self.output_dir, i, os.path.basename(x['path']), x['APPNAME'])
509                 x['out_package'] = final_app
510
511         if source.b_multi == True:
512             extra_args=[]
513             print 'THIS IS MULTI PROJECT'
514             for i, x in enumerate(source.project_list):
515                 if x['out_package'] != final_app and x.get('type') == 'app':
516                     extra_args.extend(['-r', '"%s"' % x['out_package']])
517                 elif x.get('type') == 'sharedLib':
518                     extra_args.extend(['-r', '"%s"' % x['out_package']])
519
520             extra_args.extend(['--', '"%s"' % final_app])
521             if final_app.endswith('.tpk'):
522                 out = '%s\n\n%s' % (out, self._run('package', main_args + extra_args))
523             elif final_app.endswith('.wgt'):
524                 out = '%s\n\n%s' % (out, self._run('package', main_args_web + extra_args))
525
526         #TODO: signature validation check failed : Invalid file reference. An unsigned file was found.
527         if final_app.endswith('.tpk'):
528             print 'Packaging final step again!'
529             out = '%s\n\n%s' % (out, self._run('package', main_args + ['--', '"%s"' % final_app]))
530
531         #Append arch to web binary
532         #if final_app.endswith('.wgt'):
533         #    final_app_with_arch = final_app.replace('.wgt', '-%s.wgt' % self.arch)
534         #    os.rename(final_app, final_app_with_arch)
535         #    final_app = final_app_with_arch
536
537         #Copy tpk to output directory
538         if conf == 'Debug' and manual_strip == False :
539             basename = os.path.splitext(final_app)[0]
540             if final_app.endswith('.tpk'):
541                 newname = basename +'-debug.tpk'
542             elif final_app.endswith('.wgt'):
543                 newname = basename +'-debug.wgt'
544             os.rename(final_app, newname)
545             shutil.copy(newname, source.output_dir)
546         else :
547             shutil.copy(final_app, source.output_dir)
548
549
550     def clean(self, source):
551         """SDK CLI clean command"""
552
553         if os.path.isdir(source.multizip_path):
554             shutil.rmtree(source.multizip_path)
555
556         if os.path.isfile(os.path.join(source.multizip_path, '.zip')):
557             os.remove(os.path.join(source.multizip_path, '.zip'))
558
559         for x in source.project_list:
560             self._run('clean', ['--', x['path']], show=False)
561
562 class Source(object):
563     """Project source related job"""
564
565     workspace = '' #Project root directory
566     project_list = []
567     b_multi = False
568     multi_conf_file = 'WORKSPACE' #Assume multi-project if this file exist.
569     multizip_path = '' #For multi-project packaging -r option
570     property_dict = {}
571     output_dir = '_abs_out_'
572
573     def __init__(self, src=None):
574
575         if src == None:
576             self.workspace = os.getcwd()
577         else:
578             self.workspace = os.path.abspath(src)
579         self.output_dir = os.path.join(self.workspace, self.output_dir)
580
581         os.environ['workspace_loc']=str(os.path.realpath(self.workspace))
582
583         self.multizip_path = os.path.join(self.workspace, 'multizip')
584         self.pre_process()
585
586     def set_properties(self, path):
587         """Fetch all properties from project_def.prop"""
588
589         mydict = {}
590         cp = ConfigParser.SafeConfigParser()
591         cp.optionxform = str
592         if self.is_web_app(path):
593             mydict['web_app'] = True
594         else:
595             mydict['web_app'] = False
596             cp.readfp(FakeSecHead(open(os.path.join(path, 'project_def.prop'))))
597             for x in cp.items('ascection'):
598                 mydict[x[0]] = x[1]
599         mydict['path'] = path
600         return mydict
601
602     def set_user_options(self, c_opts=None, cpp_opts=None, link_opts=None):
603         if c_opts is not None:
604             os.environ['USER_C_OPTS'] = c_opts
605             print 'Set USER_C_OPTS=[%s]' % os.getenv('USER_C_OPTS')
606         if cpp_opts is not None:
607             os.environ['USER_CPP_OPTS'] = cpp_opts
608             print 'Set USER_CPP_OPTS=[%s]' % os.getenv('USER_CPP_OPTS')
609         if link_opts is not None:
610             os.environ['USER_LINK_OPTS'] = link_opts
611             print 'Set USER_LINK_OPTS=[%s]' % os.getenv('USER_LINK_OPTS')
612
613     def is_web_app(self, project_directory):
614         if os.path.isfile(os.path.join(project_directory, 'config.xml')):
615             return True
616         return False
617
618     def pre_process(self):
619
620         if os.path.isfile(os.path.join(self.workspace, self.multi_conf_file)):
621             self.b_multi = True
622             with open(os.path.join(self.workspace, self.multi_conf_file)) as f:
623                 for line in f:
624                     if not line.strip():
625                         continue
626                     file_path = os.path.join(self.workspace, line.rstrip())
627                     self.project_list.append(self.set_properties(file_path))
628         else:
629             self.b_multi = False
630             file_path = os.path.join(self.workspace)
631             self.project_list.append(self.set_properties(file_path))
632
633 def argument_parsing(argv):
634     """Any arguments passed from user"""
635
636     parser = argparse.ArgumentParser(description='ABS command line interface')
637
638     subparsers = parser.add_subparsers(dest='subcommands')
639
640     #### [subcommand - BUILD] ####
641     build = subparsers.add_parser('build')
642     build.add_argument('-w', '--workspace', action='store', dest='workspace', \
643                         help='source directory')
644     build.add_argument('-r', '--rootstrap', action='store', dest='rootstrap', \
645                         help='(ex, mobile-3.0-device.core) rootstrap name')
646     build.add_argument('-a', '--arch', action='store', dest='arch', \
647                         help='(x86|arm|x86_64|aarch64) Architecture to build')
648     build.add_argument('-t', '--type', action='store', dest='type', \
649                         help='(tpk|wgt) Packaging type')
650     build.add_argument('-s', '--cert', action='store', dest='cert', \
651                         help='(ex, ABS) Certificate profile name')
652     build.add_argument('-c', '--conf', action='store',default='Release', dest='conf', \
653                         help='(ex, Debug|Release) Build Configuration')
654     build.add_argument('-j', '--jobs', action='store', dest='jobs', \
655                         help='(number of jobs) The number of parallel builds')
656     build.add_argument('--sdkpath', action='store', dest='sdkpath', \
657                         help='Specify Tizen SDK installation root (one time init).' \
658                              ' ex) /home/yours/tizen-sdk/')
659     build.add_argument('--profile-to-search', action='store', dest='profiletosearch', \
660                         help='Rootstrap profile prefix.' \
661                              ' ex) (mobile|wearable|da-hfp)')
662     build.add_argument('--c-opts', action='store', dest='c_opts', \
663                         help='Extra compile options USER_C_OPTS')
664     build.add_argument('--cpp-opts', action='store', dest='cpp_opts', \
665                         help='Extra compile options USER_CPP_OPTS')
666     build.add_argument('--link-opts', action='store', dest='link_opts', \
667                         help='Extra linking options USER_LINK_OPTS')
668
669     return parser.parse_args(argv[1:])
670
671 def build_main(args):
672     """Command [build] entry point."""
673
674     try:
675         my_source = Source(src=args.workspace)
676
677         my_source.set_user_options(c_opts=args.c_opts, cpp_opts=args.cpp_opts, link_opts=args.link_opts)
678         print '-------------------'
679         print '(%s)' % args.profiletosearch
680         print '-------------------'
681         my_sdk = Sdk(sdkpath=args.sdkpath, rootstrap_search=args.profiletosearch)
682         my_sdk.clean(my_source)
683         my_sdk.build_tizen(my_source, rootstrap=args.rootstrap, arch=args.arch, jobs=args.jobs)
684         if args.conf == 'Debug' :
685             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert)
686             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert, manual_strip=True)
687         else :
688             my_sdk.package(my_source, pkg_type=args.type, cert=args.cert, manual_strip=True)
689
690     except Exception as err:
691         wrk = os.path.join(os.path.abspath(args.workspace), '_abs_out_')
692         if not os.path.isdir(wrk):
693             os.makedirs(wrk)
694         with open(os.path.join(wrk, 'build_EXCEPTION.log'), 'w') as ef:
695             ef.write('Exception %s' % str(err))
696         raise err
697
698 def main(argv):
699     """Script entry point."""
700
701     args = argument_parsing(argv)
702
703     if args.subcommands == 'build':
704         return build_main(args)
705     else:
706         print 'Unsupported command %s' % args.subcommands
707         raise LocalError('Command %s not supported' % args.subcommands)
708
709 if __name__ == '__main__':
710
711     try:
712         sys.exit(main(sys.argv))
713     except Exception, e:
714         print 'Exception %s' % str(e)
715         #FIXME: Remove hard-coded output directory.
716         if not os.path.isdir('_abs_out_'):
717             os.makedirs('_abs_out_')
718         with open(os.path.join('_abs_out_', 'build_EXCEPTION.log'), 'w') as ef:
719             ef.write('Exception %s' % repr(e))
720         sys.exit(1)